diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 27ae0e75..00000000 --- a/.editorconfig +++ /dev/null @@ -1,8 +0,0 @@ -# Top-most EditorConfig file -root = true - -[*.{cs,vb}] -# Suppress S3776 (Cognitive Complexity) -dotnet_diagnostic.S3776.severity = none -# Suppress CA1416 (Platform Compatibility) -dotnet_diagnostic.CA1416.severity = none \ No newline at end of file diff --git a/.github/TradingPlatform.BusinessLayer-Miha’s MacBook Pro.dll b/.github/TradingPlatform.BusinessLayer-Miha’s MacBook Pro.dll deleted file mode 100644 index 11c3b4e3..00000000 Binary files a/.github/TradingPlatform.BusinessLayer-Miha’s MacBook Pro.dll and /dev/null differ diff --git a/.github/TradingPlatform.BusinessLayer.dll b/.github/TradingPlatform.BusinessLayer.dll index 704cc7ff..0a324bcb 100644 Binary files a/.github/TradingPlatform.BusinessLayer.dll and b/.github/TradingPlatform.BusinessLayer.dll differ diff --git a/.github/TradingPlatform.BusinessLayer.xml b/.github/TradingPlatform.BusinessLayer.xml new file mode 100644 index 00000000..47ebf0f0 --- /dev/null +++ b/.github/TradingPlatform.BusinessLayer.xml @@ -0,0 +1,6610 @@ + + + + TradingPlatform.BusinessLayer + + + + + Access to the chart panel + + + + + Chart panel unique ID + + + + + Collection of chart windows + + + + + Current X scale value - width of the bar in pixels + + + + + Provides time zone of current chart. + + + + + Provides account of current chart. + + + + + Provides custom sessions of current chart. + + + + + Current tick size of the chart + + + + + Main window of the chart + + + + + Current right offset value + + + + + Collection of chart drawingsCollection + + + + + Force chart refreshing + + + + + The MouseDown event occurs when the mouse button is pressed down + + + + + The MouseUp event occurs when the mouse button is released + + + + + The MouseClick event occurs when the mouse button is clicked + + + + + The MouseMove event occurs when the mouse moving over the chart + + + + + The MouseDown event occurs when the user scrolling mouse wheel + + + + + The MouseDown event occurs when the mouse enter the chart + + + + + The MouseDown event occurs when the mouse leave the chart + + + + + The SettingsChanged event occurs when any settings were changed + + + + + The AccountChanged event occurs when the account was changed + + + + + Access to the chart drawingsCollection collection + + + + + Add chart drawing to the collection + + + + + + Remove specified chart drawing from collection + + + + + + Get chart drawing by ID + + + + + + + Get all chart drawingsCollection assigned to specified symbol + + + + + + The Added events occured, when new chart drawing was added to collection + + + + + The Moved events occured, when chart drawing was moved + + + + + The Removed events occured, when chart drawing was removed from the collection + + + + + The SelectionChanged events occured, when selected chart drawing was changed + + + + + Access to the particular window from chart panel + + + + + Client rectangle of the chart window + + + + + Determines, whether this window is the main window of the chart + + + + + Chart window number + + + + + Special object, allows you to convert values from x/y scale to Time/Price and back + + + + + + + + + + Converter between x/y and Time/Price scales + + + + + Get the DateTime value that is corresponding to specified x coordinate + + + + + + + Get the Price value that is corresponding to specified y coordinate + + + + + + + Get the X coordinate that is corresponding to specified DateTime value + + + + + + + Get the Y coordinate that is corresponding to specified price value + + + + + + + Get the bar index that is corresponding to specified DateTime value + + + + + + + Access to the chart drawing + + + + + Determines, the way how chart drawing was created: manually or programmatically + + + + + Determines, the availability of drawing - only current chart or all charts with same symbol + + + + + The unique ID of the chart drawing + + + + + Determines, whether chart drawing draws above or below the main chart + + + + + Determines, state of the chart drawing: Locked or Unlocked + + + + + Get time and price of the particular point of the chart drawing + + + + + Set time and price value for particular point of the chart drawing + + + + + Gets s list + + + + + Gets symbol types list + + + + + Gets s list + + + + + Gets s list + + + + + Gets Exchanges list + + + + + Gets Orders list + + + + + Gets Order Types list + + + + + Gets Positions list + + + + + Gets Closed Positions list + + + + + Gets Corporate Actions list + + + + + Gets Report Types list + + + + + Gets s list + + + + + Vendor connection respond bundle + + + + + The connection status + + + + + The respond message + + + + + Ping state dictum + + + + + Ping signals connected + + + + + Ping signals disconnected + + + + + The asset formatting description. + + + + + Initializes a new instance of the class. + + The asset id. + The value. + If true, add currency name. + + + + Mediates a history meta data with available data types and intervals on vendor side + + + + + History data types + + + + + History intervals + + + + + History intervals + + + + + Asset id bearer + + + + + Asset name bearer + + + + + Asset description + + + + + Asset id bearer + + + + + Performs a binding with broker or data provider + + + + + Specifies any operation before breaking a connection with broker or data provider + + + + + Called when platform finished retreiving all required informations from vendor during connecting + + + + + Ping processing before its visualization in the terminal + + + + + Retrieves an information about available accounts. Yon need to specify at least one account + + + + + Confirms allowed nonfixed list by vendor + + + + + Retrieves a collection of available exchange markets from vendor + + + + + Retrieves information about available assets. + + + + + Retrieves information about available symbols. + + + + + Gets an available symbols types from vendor + + + + + Derives a non fixed symbol from vendor + + + + + Derives a non-fixed list of symbols from vendor + + + + + Retrieves allowed/supported order types + + + + + Retrieves an information about opened orders at the time of connection. + + + + + Retrieves an information about positions at the time of connection + + + + + Gets trades history from server for requested time range + + + + + Subscribing to quote data: Level1/Level2/Trade + + + + + Unsubscribing from quote data: Level1/Level2/Trade + + + + + Gets a list of supported historical intervals and data types by vendor + + + + + Prepare and sending order placing request to broker + + + + + Sending order modification request to broker + + + + + Sending order cancellation request to broker + + + + + Sending position closing request to broker + + + + + Gets information about available reports from vendor + + + + + Called when platform need to generate particular report + + + + + Ping respond bundle + + + + + Ping time + + + + + Time spent for single request + + + + + Ping state + + + + + Performs a binding with broker or data provider + + + + + Specifies any operation before breaking a connection with broker or data provider + + + + + Called when platform finished retreiving all required informations from vendor during connecting + + + + + Ping processing before its visualization in the terminal + + + + + Retrieves an information about available accounts. Yon need to specify at least one account + + + + + Retrieves a collection of available exchange markets from vendor + + + + + Retrieves information about available assets. + + + + + Retrieves information about available symbols. + + + + + Gets an available symbols types from vendor + + + + + Derives a non fixed symbol from vendor + + + + + Derives a non-fixed list of symbols from vendor + + + + + Retrieves an information about opened orders at the time of connection. + + + + + Retrieves an information about positions at the time of connection + + + + + Retrieves an information about closed positions at the time of connection + + + + + Subscribing to quote data: Level1/Level2/Trade + + + + + Unsubscribing from quote data: Level1/Level2/Trade + + + + + Gets a list of supported historical intervals and data types by vendor + + + + + Prepare and sending order placing request to broker + + + + + Sending order modification request to broker + + + + + Sending order cancellation request to broker + + + + + Sending position closing request to broker + + + + + Gets information about available reports from vendor + + + + + Called when platform need to generate particular report + + + + + Retrieves allowed/supported order types + + + + + Confirms allowed non fixed list by vendor + + + + + Gets a settings list from a vendor selection of the setup window + + + + + The ID of the order group. This group created when trades done by the MAM account. + + + + + Total quantity of the order + + + + + Gets OrderType + + + + + Gets order price value + + + + + Gets order trigger price value + + + + + Gets order trailing offset value + + + + + Gets orders current status + + + + + Gets orders last update time + + + + + Gets Position Id. + + + + + Gets StopLoss holder for given order + + + + + Gets TakeProfit holder for given order + + + + + Orders Type Id. It is used for the orders type comparing. + + + + + Gets order TIF(Time-In-Force) type + + + + + Gets orders expiration time + + + + + Remaining quantity of the order + + + + + Filled quantity of the order + + + + + Gets open order original status + + + + + Will be triggered on each invocation + + + + + + + + + + + + + + + Get VWAP value + + + + + + + + + + + + + + + + + + + + + + + + + Снапшот для эмулятора + + + + + Текущая версия для сериализации. Если что-то меняем и нужно делать поддержку старого формата + можно использовать версию. + + + + + Version 1.1: изменилась схема работы с коннектами: стали доступны кастомные коннекты + + + + + Defines 'Volume Analysis' calculation result item + + + + + Contains all user's account information + + + + + Gets account unique code. + + + + + Obtaining account name. + + + + + Gets base currency of account. Account CCY is always equal to the server CCY in AlgoStudio + + + + + Gets current balance of the account. + + + + + Gets additional account information + + + + + Will be triggered on each account information updating + + + + + Gets Account name + + + + + + Creates a business object info with an Account data which can be used for the restoring/serialization process. + + + + + + The account operation. + + + + + Gets the name. + + + + + Gets the button text. + + + + + Gets the settings. + + + + + + + The settings. + + + + Builds the confirmation. + + The settings. + A string. + + + + + + The settings. + + + + Compare to. + + The obj. + An int. + + + + The additional info collection. + + + + + Gets the count. + + + + + Gets the items. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + The items. + + + + Try get item. + + The api key. + The item. + A bool. + + + + Gets the enumerator. + + ]]> + + + + Tos the string. + + A string. + + + + The comparing type. + + + + + The additional info item formating type. + + + + + The additional info item. + + + + + ключ для апи, обязательно для заполнения и должен быть уникальным + + + + + Gets or Sets the group info. + + + + + Gets or Sets the sort index. + + + + + Gets or Sets the name key. + + + + + Gets or Sets the tool tip key. + + + + + Gets or Sets the data type. + + + + + Gets or Sets the value. + + + + + Gets or Sets a value indicating whether hidden. + + + + + Gets or Sets the formating type. + + + + + используется в связке с AccountAdditionalInfoItemFormatingType.CustomAsset для форматирования в заданном ассете + + + + + Gets or Sets a value indicating whether visible. + + + + + Gets or Sets a value indicating whether is link. + + + + + Gets or Sets the editing info. + + + + + Gets or Sets the formatting description. + + + + + Initializes a new instance of the class. + + + + + + + The item. + + + + + + An object. + + + + To the XML element. + + A XElement. + + + + From the XML element. + + The element. + The deserialization info. + + + + To the string. + + A string. + + + + Defines asset entity + + + + + Asset id bearer + + + + + Asset name bearer + + + + + Asset description + + + + + Defines a number precision of the change value + + + + + Gets precision value + + + + + Gets asset ISO 4217 code + + + + + Creates an Asset instance + + given connection Id + + + + Formats price into precision normalized string + + + + + + + Formats price into concatenated string which contains the precision normalized value and Asset's name + + + + + + + Gets Asset name + + + + + + Uses comparison by Assets names + + + + + + + Unique ID during active session. Don't use for serialization + + + + + Договориль использовать множественное число для типа инструмента + + + + + Завели для индийской интеграции + + + + + Represents information about corporate action. + + + + + Get the date and time when trade was executed + + + + + Will be triggered on corporate action updating + + + + + Base class for all Custom symbols: Synthetics, Custom Formulas, etc. + + + + + alexb: нам достаточно подставлять правильный HistoricalData - он сделает всю работу + + + + + Расчёт Bid/Ask/BidSize/AskSize для синтетика + + + + + Расчёт Last/LastSize для синтетика + + + + + Расчёт Level2 для синтетика + + + + + Расчёт DayBar для синтетика + + + + + Расчёт бара для синтетика + + + + + Increment current position + + + + + Will be triggered on each invocation + + + + + Applies an position modificaion or position changing by accepting a message. + + + + + + Contains all information which belong to the given exchange + + + + + Gets Exchange Id + + + + + Gets Exchange name + + + + + Used for the Exchanges comparing + + + + + Compares Exchnges in order by next - SortIndex, Exchange name, Connection Id, Exchange Id. + + + + + + + Provides possibility to group and sort symbols for each connection + + + + + Gets group Id + + + + + Gets group name + + + + + Gets sort index for comparing process + + + + + Represents trading information about pending order + + + + + The ID of the order group. This group created when trades done by the MAM account. + + + + + Gets Position Id. + + + + + Total quantity of the order + + + + + Filled quantity of the order + + + + + Remaining quantity of the order + + + + + Orders Type Id. It is used for the orders type comparing. + + + + + Gets OrderType + + + + + Gets order price value + + + + + Gets order trigger price value + + + + + Gets order trailing offset value + + + + + Gets orders current status + + + + + Gets open order original status + + + + + Gets order TIF(Time-In-Force) type + + + + + Gets orders expiration time + + + + + Gets orders last update time + + + + + Gets StopLoss holder for given order + + + + + Gets TakeProfit holder for given order + + + + + Will be triggered on each invocation + + + + + Creates Order instance + + + + + + Applies an order modificaion or order changing by accepting a message. + + + + + + Cancels pending order + + + + + + Represents an wrapper + + + + + Represents trading information about related position + + + + + Will be triggered on each and invocation + + + + + Gets position quantity value + + + + + Gets position open order price + + + + + Gets position openning time + + + + + Gets Profit/loss (without swaps or commissions) all calculated based on the current broker's price. For open position it shows the profit/loss you would make if you close the position at the current price. If position closed, this parameter show profit/loss what trader have after closing this position. + + + + + Gets Profit/loss calculated based on the current broker's price. For open position it shows the profit/loss you would make if you close the position at the current price. If position closed, this parameter show profit/loss what trader have after closing this position. + + + + + Gets fee amount for the position. + + + + + Gets PnL swaps + + + + + The market price obtainable from your broker. + + + + + Closes position if quantity is not specified else - uses partial closing operation. + + + + + + + Gets StopLoss order which belongs to the position + + + + + Gets TakeProfit order which belongs to the position + + + + + Returns ticks amount between open and current price. + + + + + Represents a report grid which can hold system reports based on s and s content. + + + + + Columns collection + + + + + Rows collection + + + + + Initializes report grid + + + + + Adds coloring column to the report + + + + + Adds default(non-colored) column to the report + + + + + Exposes report cell decoration + + + + + Cell label + + + + + Cell value + + + + + Exposes report column decoration + + + + + Header of the column + + + + + Column value type + + + + + Exposes report row decoration + + + + + A collection of cells + + + + + Initializes cells in the row + + + + + Adds cells to the row by label and value + + + + + Adds cells to the row by value only + + + + + Defines report request parameters from which can be used in + + + + + Gets report Id + + + + + Gets report Name + + + + + report settings + + + + + Represents a permisions checking tool which use next priority order , , and + + + + + Represent access to symbol information and properties. + + + + + Gets symbol Id + + + + + Gets symbol name + + + + + Gets symbol description + + + + + Gets symbol type + + + + + Gets symbol base Asset + + + + + Gets symbol counter Asset + + + + + Gets Exchange of current symbol + + + + + Gets Exchange id of current symbol + + + + + Returns delay with which quote come in platform. + + + + + Gets symbol additional info + + + + + The highest trade allowed + + + + + The lowest trade allowed + + + + + Gets symbol NettingType + + + + + Gets SymbolGroup + + + + + Gets current SymbolQuotingType + + + + + Amount of base asset for one lot. + + + + + Stores list of symbol ticksizes + + + + + Cached value of TickSize (if 1 level) + + + + + Step of the lot changes + + + + + Step of the notional value changes + + + + + Gets derivative expiration date + + + + + Gets derivative last trading date + + + + + Gets derivative maturity date + + + + + Gets derivative strike price + + + + + Gets derivative option style + + + + + Gets derivative option type + + + + + Gets derivative option serie + + + + + Gets derivative underlier name + + + + + Gets derivative underlier symbol + + + + + Gets derivative underlier symbol id + + + + + Gets Ask price + + + + + Gets Ask size + + + + + Gets Bid price + + + + + Gets Bid size + + + + + Gets quote time + + + + + Gets last price + + + + + Gets last size + + + + + Gets last time + + + + + Gets mark price + + + + + Gets mark size + + + + + Gets open price + + + + + Gets previous close price + + + + + Gets high price + + + + + Gets low price + + + + + Gets volume value + + + + + Gets quote asset volume value + + + + + Gets PrevSettlement value + + + + + Gets ticks amount + + + + + Gets trades amount + + + + + Gets Level2 data + + + + + Gets spread value between Bid and Ask + + + + + Gets percentage value + + + + + Gets change value between Bid/Last and Close price + + + + + Gets percentage value + + + + + Default history type + + + + + List of all available history types + + + + + Gets SymbolVolumeType + + + + + List of all available history types + + + + + Will be triggered when new Level1 quote is comming + + + + + Will be triggered when new Level2 quote is comming + + + + + Will be triggered when new trade quote is comming + + + + + Will be triggered when new correctional quote is comming from the vendor. + + + + + Will be triggered when symbol updated. + + + + + Gets real time calculation setting + + + + + Gets real time calculation setting + + + + + Gets real time calculation setting + + + + + Gets real time calculation setting + + + + + Gets historical data according to period and other parameters + + + + + + + + + Gets historical data according to period and other parameters + + + + + + + + + + Gets historical data according to aggregation and other parameters + + + + + + + + + + Gets historical data according to given history request + + + + + + + Gets historical ticks data according to given parameters + + + + + + + + + Returns rounded to price + + + + + Calculates new price which equal to given price shifted by a number of given ticks + + + + + + + + Calculates ticks between two prices + + + + + + + + Gets cached symbol tick size or retrives it from the list + + + + + + + Gets symbol tick cost retrived from the list by price + + + + + + + Returns VariableTick if it can be retrived from list by price or null + + + + + + + Gets cached tick size if it available, else tries to obtain with Last, Bid, Ask, first element of list otherwise - + + + + + Formats price value to the appropriative string with a counting on tick precision. + + + + + + + Formats price value to the appropriative string with a counting on max tick precision. + + + + + + + Returns string with formatted ticks value + + + + + + + + Comparing by value + + + + + Gets symbol orders types list which are allowed. + + + + + + + Process order placing with given request parameters + + + + + + + An symbol has possibility to obtain the default account + + + + + Represents information about trade. + + + + + Gets the unique identifier of the order initiating the trade. + + + + + Gets a unique identifier of the position, which is related to this trade. + + + + + Get the price where trade was executed + + + + + Get the trade quantity + + + + + Get the date and time when trade was executed + + + + + Get the trade Gross P&L + + + + + Get the trade Net P&L + + + + + Get the fee value that was charged for this trade + + + + + Get the trade order type + + + + + Will be triggered on trade updating + + + + + The symbols list manager. + + + + + Gets the count. + + + + + Gets or Sets the settings. + + + + + Represent access to level2 data. + + + + + Gets Level2 Asks list + + + + + Gets Level2 Bids list + + + + + Gets current Level2 data + + Parameters of DepthOfMarket + + + + + Gets current Level2 data + + Parameters of request for Leve2Item collection + + + + + Leve2 data. Contains Bids and Ask collections + + + + + Represent parameters of DepthOfMarket + + + + + Represent parameters of request for Leve2Item collection + + + + + Aggregation method + + + + + Required amount of level2 + + + + + Use custom tick size + + + + + Calculate cumulative size + + + + + Aggregation method + + + + + Represent access to level2 item. + + + + + Cumulative size + + + + + Imbalance Percent + + + + + Price + + + + + Size + + + + + Time + + + + + MMID + + + + + проверка на вхождение + + + + + Represents information about connection and provides an access to the current trading information(Symbols, Orders, Position, Accounts etc.). + + + + + Provides access to all business objects which are belong to this connection + + + + + Gets connection Id + + + + + Gets connection Name + + + + + Gets connection's vendor name + + + + + Contains list of connection settings. Will be reused on each population time. + + + + + Gets connection's state (Connected/Connecting/Fail etc.) + + + + + Defines connection type + + + + + Will be triggered when changed. + + + + + Will be triggered when changed. + + + + + Represents connection ping time + + + + + Messages count that one is waited to process + + + + + Gets a matched available metadata info with the vendor's side + + + + + Establishes a connection to a specified vendor + + + + + + Closes a connection. + + + + + Только для использования в ConnectionCache + + + + + alexb: "Умный" поиск - разрешаем указывать слова в любом порядке + + + + + Generates a report with given requested parameters + + specifies Report's type + + + + + Gets connection's s list + + + + + Gets connection's symbol types list which are allowed in this connection + + + + + Gets connection's s list + + + + + Gets connection's s list + + + + + Gets all Exchanges which are allowed in this connection + + + + + Gets all Orders which are belong to this connection + + + + + Gets all Order Types which are available on this connection + + + + + Gets all Positions which are belong to this connection + + + + + Gets all Closed Positions which are belong to this connection + + + + + Gets all Corporate actions which are belong to this connection + + + + + Gets all Report Types which are available on this connection + + + + + Gets all Account operations which are available on this connection + + + + + Gets all Tradins signals which are available on this connection + + + + + Server connection status + + + + + Specifies connection using type. + + + + + Specifies how connection was created: by default or by user + + + + + Represents all needed parameters for the connection constructing process. + + + + + Gets a user friendly name of the connection + + + + + Gets connection group + + + + + Gets vendor's name + + + + + Gets connection Id + + + + + Favorites one will be displayed in Control center toolbar + + + + + Gets ConnectionState + + + + + Gets vendor's settings + + + + + Specifies how connection was created: by default or by user + + + + + realization + + + + + Creates connection info instance + + + + + + Creates connection info instance. + + + + + + + + + дефолтовый период пигования + + + + + Таймер, по которому проходит пинг + + + + + Запускаем таймер + + + + + Останавливаем таймер + + + + + Коннекты, которые нужно подключить + + + + + Сколько ждем до следующей попытки + + + + + The main entry point in the API. Core keeps access to all business logic entities and their properties: + connections, accounts, symbols, positions, orders, etc. Some of them can be reached through using managers or directly via specified collections. + You can always access the Core object via static Core.Instance property. + + + + + Gets a singleton instance of . API entry point + + + + + Gets an access to all created connections and manages them + + + + + Gets an access to the system logging mechanism + + + + + Obtains licence rules for the current user + + + + + Gets an access to all available trading data vendors and creates them + + + + + Gets an access to the all available aggregation types + + + + + Access to Volume Analysis calculations + + + + + Gets an access to the all available indicators and creates them + + + + + Gets an access to the all available trading strategies and manages them + + + + + Gets a permissions checking mechanism + + + + + Gets a time based conversion and synchronization mechanism + + + + + Gets SMTP mail service for sending emails + + + + + Represents current trading status + + + + + Will be triggered when changed + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections + + + + + Gets all available s from open connections. Otherwise returns empty list + > + + + + Gets all available s from open connections. Otherwise returns empty list + > + + + + Starts a initialization process which initializes given Managers, and Utils. And provides a subscribing on events. + + + + + Disposes all previously initialized modules and unsubscribe from events + + + + + Will be triggered when new added to the core + + + + + Gets an instance of exist Account or creates a new one with given info parameter + + + + + + + Will be triggered when new added to the core + + + + + Returns all s from open connections which satisfy given request parameters, otherwise returns empty list + + + + + + + Retrieves any by given request parameters. Otherwise returns null + + + Must be specified if open connections total is more than one. Will search only in Synthetic symbols list if id is equal to + + + + + + Gets an instance of exist symbol or creates a new one with given info parameter + + + + + + + Will be triggered when new placed + + + + + Will be triggered when canceled + + + + + Gets instance by given Id string. Otherwise returns null + + + Must be specified if open connections total is more than one + + + + + Gets instance by given Id string. Otherwise returns null + + + Must be specified if open connections total is more than one + + + + + Will be triggered when new opened + + + + + Will be triggered when closed + + + + + Gets instance by given Id string. Otherwise returns null + + + Must be specified if open connections total is more than one + + + + + Gets Profit'n'Loss with given request parameters from open connection. Otherwise returns null + + + + + + + Will be triggered when new added + + + + + Will be triggered when removed + + + + + Will be triggered when new occured + + + + + Gets collection of by given parameters + + + + + + + + Gets collection of by given parameters and callback + + + + + + + + + Will be triggered when new occured + + + + + Will be triggered when new added + + + + + Gets collection of by given parameters + + + + + + + + Places with given request parameters + + + + + + + Places multiple s with given request parameters + + + + + + + + Modifies by given request parameters + + + + + + + Closes with given request parameters + + + + + + + Cancels with given request parameters + + + + + + + Returns with given request parameters from open connection + + + + + + + Will be triggered when new received + + + + + Sends custom request if connection with given Id is open + + + + + + + Subscribe on custom messages + + custom message handler + custom messages Id + + + + Unsubscribe from custom messages + + custom message handler + custom messages Id + + + + Gets all previously configured s + + + + + Gets an access to s and manages them + + + + + Adds if given parameters are valid + + + + + + + Replaces exist list by new list in if given parameters are valid + + + + + + + Removes with a given name + + + + + + Replaces list name by new given name in if given parameters are valid + + + + + + + Represent all available Historical Symbols + + + + + Will be triggered when custom symbol added + + + + + Will be triggered when custom symbol removed + + + + + Will be triggered when custom symbol updated + + + + + Represent all available Synthetic items + + + + + Adds given Synthetic to the list if it does not contain + + + + + + Removes given Synthetic from the list if it exists + + + + + + Will be triggered when created/chenged/removed + + + + + Override to change string representation of aggregation + + + + + Represent access to historical data information and indicators control. + + + + + Gets HistoricalData symbol + + + + + Gets HistoricalData Period + + + + + Gets HistoricalData aggregation + + + + + Gets HistoricalData history type + + + + + Gets HistoricalData left time boundary + + + + + Gets HistoricalData right time boundary + + + + + Gets HistoricalData items amount + + + + + Retrieves HistoricalData item by indexing offset and direction to find. + + + + + + + + Will be triggered when new historical item created + + + + + Will be triggered when current historical item changed or updated + + + + + Gets access to built-in indicators + + + + + Reloads entire HistoricalData + + + + + Gets index by time with counting on search direction + + + + + Gets array of attached indicators + + + + + Creates indicator by it's name and if it successfully created adds it to the HistoricalData + + + + + + + + Adds indicator to the HistoricalData + + + + + + Removes indicator from the HistoricalData + + + + + + Will be triggered when volume analysis of current historical item changed or updated + + + + + Period that can be used as a basis for history aggregations + + + + + Represents historical data bar item + + + + + Gets bar's right time border + + + + + Defines bar's ticks count + + + + + Defines Open price + + + + + Defines High price + + + + + Defines Low price + + + + + Defines Close price + + + + + Gets Median (High+Low)/2 price + + + + + Gets Typical (High+Low+Close)/3 price + + + + + Gets Weighted (High+Low+Close+Close)/4 price + + + + + Defines ticks amount + + + + + Defines volume value + + + + + Gets price by indexing + + + + + + + Creates HistoryItemBar instance with default OHLC price = + + + + + Comparing by , OHLC, + + + + + + + Creates HistoryItemLast instance + + + + + Represents historical data trade item + + + + + Defines price value + + + + + Defines volume value + + + + + Defines trade operation side as aggressor flag + + + + + Gets price by indexing + + + + + + + Creates HistoryItemLast instance + + + + + Comparing by , OHLC, + + + + + + + Creates HistoryItemLast instance + + + + + Gets price by indexing + + + + + + + Creates HistoryItemLast instance + + + + + Represents historical data tick item + + + + + Defines Bid price + + + + + Defines Bid size + + + + + Defines Ask price + + + + + Defines Ask size + + + + + Gets price by indexing + + + + + + + Creates HistoryItemBar instance with default Ask/AskSize/Bid/BidSize = + + + + + Comparing by , OHLC, , , , + + + + + + + Resolves a history request parameters per symbol + + + + + Represents mechanism for supporting predefined and custom periods + + + + + Gets period multiplier + + + + + Gets base period type + + + + + Gets ticks value as an result of base period multiplicated by + + + + + Creates Period instance with greater than 0 + + + + + + + True if their base paeriods and are respectively equal. + + + + + + + + True if any of their base paeriods and are respectively not equal. + + + + + + + + True if their ticks values are satisfying initial condition + + + + + + + + True if their ticks values are satisfying initial condition + + + + + + + + True if their ticks values are satisfying initial condition + + + + + + + + True if their ticks values are satisfying initial condition + + + + + + + + True if their base paeriods and are respectively equal. + + + + + + + Formats given value to a specific user friendly string + + + + + + + Returns value in ticks according to base period type + + + + + + + Returns shorted string according to base period type + + + + + + + Converts time gap into dates range + + + + + + + Compares by + + + + + + + Serialize into object + + + + + + Deserialize from + + + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Predefined period + + + + + Represent access to DayBar quote, which contains summary information about instrument prices. + + + + + High price + + + + + Open price + + + + + Low price + + + + + Previous Close price + + + + + Ticks value + + + + + Volume value + + + + + Volume value + + + + + Previous settlement price + + + + + Bid price + + + + + Bid size + + + + + Ask price + + + + + Ask size + + + + + Last price + + + + + Last size + + + + + Trades value + + + + + Change value + + + + + Change value in percentage + + + + + + + + + + Represent access to DOM2 quote, which contains Bids and Asks. + + + + + Collection of Asks quotes + + + + + Collection of Bids quotes + + + + + Represent access to trade information. + + + + + Price at which trade occured + + + + + Size of the trade + + + + + Information about operation side of the trade + + + + + Shows the direction of price movement, comparing to previous value. + + + + + Represent access to Level2 quote. + + + + + Price type of Level2 quote: Bid or Ask + + + + + Price of Level2 quote + + + + + Size of Level2 quote + + + + + Unique ID of Level2 quote + + + + + Shows, whether Level2 quote is using only for removing from depth + + + + + Broker identifier that send level2 quote + + + + + specifies the implied quantity associated with the price for the quote. Subtracting this amount from the Size yields the outright quantity for the price level. A value of zero indicates that the implied size is not available/defined or that it is actually zero. + + + + + Time of the quote + + + + + Represent access to quote information. + + + + + Bid price + + + + + Bid size + + + + + Ask price + + + + + Ask size + + + + + Shows the direction of bid price movement, comparing to previous value. + + + + + Shows the direction of ask price movement, comparing to previous value. + + + + + Constants for licence keys + + + + + User licences info store + + + + + Current state of connection to licence server + + + + + Current connected user info + + + + + All active licences that user have + + + + + Check that user have licence + + + + + The loopback http listener without kestrel. + + + + + Initializes a new instance of the class. + + The port. + + + + Dispose + + + + + Wait for callback asynchronously. + + The token. + ]]> + + + + Specifies the reason of price data updating. + + + + + Indicates the processing of new historical bar. + + + + + Indicates the coming of new real-time tick. + + + + + Indicates a start of new real-time bar. + + + + + Specifies the type of events when indicator should recalculate itself. + + + + + Specifies the style of indicator line. + + + + + Moving average mode + + + + + Simple Moving Average + + + + + Exponential Moving Average + + + + + Smoothed Moving Average + + + + + Linearly Weighted Moving Average + + + + + + + + + + Displays HV schedule and percentile value. + + + + + Displays the percentile schedule only. + + + + + Returns an instance of the Exponential Moving Average (EMA) indicator. + EMA provides a weighted price calculation for the last N periods. + + Period of Exponential Moving Average + Sources prices for MA + Calculation type + + + + Gets the Linearly Weighted Moving Average + Linear Weighted Moving Average makes the most recent bar more important unlike SMA. + + Moving average period + Type of the price + + + + + Gets the SMA(Simple Moving Average) indicator. + The 'SMA' indicator provides an average price for the last N periods. + + Period of simple moving average. + Sources prices for MA. + + + + + Returns an instance of the Smoothed Moving Average (SMMA) indicator. + SMMA indicator provides a smoothed average price for the last N periods. + + Moving average period + Type of the price + Calculation type + + + + + Gets the PPMA(Pivot Point Moving Average) indicator. + The 'PPMA' indicator uses the pivot point calculation as the input a simple moving average. + + Period of PPMA indicator + + + + + + Gets the MAS3 (3MASignal) indicator. + The 'MAS3' indicator offers buy and sell signals according to intersections of three moving averages. + + Short moving average period. + Middle moving average period. + Long moving average period. + The count of bars. The trend will be determined on this interval. + + + + + + Gets the specific MA indicator, according to selected 'MaMode'. + + Period of moving average. + Type of price. + MA mode. + Calculation type + + + + + + Gets the BB(Bollinger Bands) indicator. + The 'BB' indicator provides a relative definition of high and low based on standard deviation and a simple moving average. + + Period of MA for envelopes. + Value of confidence interval. + Sources prices for MA. + Type of moving average. + Calculation type + + + + + Gets the Commodity Channel Index. + Measures the position of price in relation to its moving average. + + Period for CCI MA + Sources prices for CCI + MA mode for CCI + Calculation type + + + + + Returns an instance of the Modified Moving Average (MMA) indicator. + MMA comprises a sloping factor to help it overtake with the growing or declining value of the trading price of the currency. + + Period of Modified Moving Average + Sources prices for MA + + + + + Gets the Regression indicator + The Linear Regression Indicator plots the ending value of a Linear Regression Line for a specified number of bars; showing, statistically, where the price is expected to be. + + Moving average period + Type of the price + + + + + Gets the Channel (Price Channel) indicator. + The 'Channel' indicator is based on measurement of min and max prices for the definite number of periods. + + Period of price channel + + + + + + Gets the AFIRMA indicator + Autoregressive finite impulse response moving average. A digital filter accurately shows the price movement as powered with least square method to minimise time lag + + Moving average period + Type of the price + Afirma mode + with least squares method overlapping if true + + + + + Gets the RSI indicator. + Relative Strength Index (RSI) is a momentum oscillator that measures the speed and change of price movements. + + RSI Period + Price Type + RSI Mode (Simple or Exponential) + MA Mode for smooth data + MA period for smooth data + Calculation type + + + + + Returns an instance of the McGinley Dynamic indicator. + McGinley Dynamic avoids of most whipsaws and it rapidly moves up or down according to a quickly changing market. It needs no adjusting because it is dynamic and it adjusts itself. + + Period of exponential moving average + Dynamic tracking factor + Source price type + Calculation type + + + + + Gets the MAE (Moving Average Envelope) indicator. + The 'MAE' indicator demonstrates a range of the prices discrepancy from a Moving Average. + + Period of MA for envelopes. + Sources prices for MA. + Type of moving average. + Upband deviation in %. + Downband deviation in %. + Calculation type + + + + + Returns an instance of the Parabolic Time/Price System (SAR) indicator. + SAR indicator helps to define the direction of the prevailing trend and the moment to close positions opened during the reversal. + + Step of parabolic SAR system + Maximum value for the acceleration factor + + + + + + Gets the AO (Awesome Oscillator) indicator. + The 'AO' indicator determines market momentum. + + + + + + Gets the CMO (Chande Momentum Oscillator) indicator. + The CMO calculates the dividing of difference between the sum of all recent gains and the sum of all recent losses by the sum of all price movement over the period. + + Period of MA for envelopes. + Sources prices for MA. + + + + + + Returns an instance of the ZigZag indicator. + ZigZag is a trend following indicator that is used to predict when a given symbol's momentum is reversing. + + Percent Deviation + + + + + + Gets the Aroon indicator. + Reveals the beginning of a new trend and determines how strong it is + + Aroons period + + + + + + Returns an instance of the Standart Deviation (SD) indicator. + The SD shows the difference of the volatility value from the average one. + + Period of indicator + Sources prices for MA + Type of Moving Average + Calculation type + + + + + Gets the MACD (Moving Average Convergence/Divergence) indicator. + The MACD is a trend-following momentum indicator that shows the relationship between two moving averages of prices. + + Period of fast EMA. + Period of slow EMA. + Period of signal EMA. + Calculation type + + + + + Returns an instance of the Kairi Relative Index (KRI) indicator. + KRI calculates deviation of the current price from its simple moving average as a percent of the moving average. + + + + + + + + Returns an instance of the Bollinger Bands Flat (BBF) indicator. + The BBF provides the same data as BB, but drawn in separate field and easier to recognize whether price is in or out of the band. + + Period + Deviation + Sources prices for MA + Type of Moving Average + Calculation type + + + + + Gets the ROC (Rate of Change) indicator. + The ROC shows the speed at which price is changing. + + Period of momentum. + + + + + + Gets the Momentum indicator. + Momentum compares where the current price is in relation to where the price was in the past. + + Period for Momentum + Sources prices for Momentum + + + + + + Gets the %R Larry Williams. + Uses Stochastic to determine overbought and oversold levels. + + Period for Momentum + + + + + + Returns an instance of the Price Oscillator (PO) indicator. + PO calculates the variation between price moving averages. + + Period of MA1 + Period of MA2 + Sources prices for MA + Type of Moving Average + Calculation type + + + + + Gets the OsMA (Moving Average of Oscillator) indicator. + The OsMA reflects the difference between an oscillator (MACD) and its moving average (signal line). + + Period of fast EMA. + Period of slow EMA. + Period of signal EMA. + Calculation type + + + + + Gets On Balance Volume. + On Balance Volume (OBV) measures buying and selling pressure as a cumulative indicator that adds volume on up days and subtracts volume on down days. + + Sources prices for OBV + + + + + + Returns an instance of the Positive Volume Index (PVI) indicator. + The PVI value changes on the periods in which value of volume has increased in comparison with the previous period. + + + + + + + + Returns an instance of the Volume indicator. + Volume allows to confirm the strength of a trend or to suggest about it's weakness. + + + + + + + Gets the MFI(Money Flow Index) indicator. + The MFI(Money Flow Index) is an oscillator that uses both price and volume to measure buying and selling pressure. + + Period of MFI. + + + + + + Returns an instance of the Acceleration/Deceleration Oscillator (AC). + AC measures the acceleration and deceleration of the current momentum. + + + + + + + Gets the Average True Range (ATR) indicator. + The ATR measures of market volatility. + + + Period of Moving Average. + Type of Moving Average + Calculation type + + + + + Returns an instance of the Kaufman Adaptive Moving Average (KAMA) indicator. + KAMA is an exponential style average with a smoothing that varies according to recent data. + + Period + Fast factor + Slow factor + Sources prices for MA + + + + + + Returns an instance of the Qstick indicator. + The Qstick is a moving average that shows the difference between the prices at which an issue opens and closes. + + + + Calculation type + + + + + Get the Swing Index (SI) indicator. + The SI is used to confirm trend line breakouts on price charts. + + The divider. + + + + + + Get the True Strength Index (TSI) indicator. + + The TSI is a variation of the Relative Strength Indicator which uses a doubly-smoothed + EMA of price momentum to eliminate choppy price changes and spot trend changes. + + + First MA period. + Second MA period. + Calculation type + + + + + Returns an instance of the Average Directional Index (ADX) indicator. + The ADX determines the strength of a prevailing trend. + + Period + Type of Moving Average + Calculation type + + + + + Returns an instance of the Keltner Channel indicator. + Keltner Channels are volatility-based envelopes set above and below an exponential moving average. + + Period of MA for Keltner's Channel + Coefficient of channel's width + Sources prices for MA + Type of Moving Average + Calculation type + + + + + Returns an instance of the Percentage Price Oscillator (PPO). + Percentage Price Oscillator is a momentum indicator. Signal line is EMA of PPO. Formula: (FastEMA-SlowEMA)/SlowEMA. + + Fast EMA Period + Slow EMA Period + Signal EMA Period + Calculation type + + + + + Gets the Stochastic Slow. + Shows the location of the current close relative to the high/low range over a set number of periods (Slow). + + Period + Smoothing + Double smoothing + Moving type + Calculation type + + + + + Gets the Stochastic x Relative Strength Index. + StochRSI is an oscillator that measures the level of RSI relative to its range. + + Period + Smoothing + Double smoothing + + + + + + Gets the Ichimoku. + Enables to quickly discern and filter 'at a glance' the low-probability trading setups from those of higher probability. + + Tenkan Period + Kijun Period + Senkou Span B + + + + + + Gets the Directional Movement Index(DMI) indicator. + The DMI іdentifies whether there is a definable trend in the market. + + Period of Moving Average. + Type of Moving Average. + Calculation type + + + + + Gets the Alligator. + Three moving averages with different colors, periods and calculation methods. + + Type of Jaw Moving Average. + SourcePrice of Jaw Moving Average. + Period of Jaw Moving Average. + Shift of Jaw Moving Average. + Period of Moving Average. + Type of Moving Average. + Period of Moving Average. + Type of Moving Average. + Period of Moving Average. + Type of Moving Average. + Period of Moving Average. + Type of Moving Average. + + + + + + Base class for all indicators. + + + + + Short name of indicator + + + + + Access to current Symbol of indicator + + + + + Amount of items in internal buffers + + + + + Represent access to current used historical data. + + + + + Represent access indicator series + + + + + + + + + + Specified, whether indicator should use main or additional window on the chart + + + + + Specified, whether indicator should draw on chart background by default. + + + + + Specified, whether indicator should participate into price auto scale system. + + + + + Precision amount for formatting price (the count of digits after decimal point); By default = -1, which means to use precision from indicator's symbol + + + + + Indicator's settings + + + + + Represent access to the chart, that created indicator + + + + + Recalculate indicator + + + + + + + + + + + Sets the value of indicator into internal buffer + + Value + Index of indicator line + Offset value + + + + Gets the value of indicator from internal buffer + + Offset value + Index of indicator line + Offset start point + + + + + Set line break point. + + Offset value + Index of indicator line + Offset start point + + + + Remove line break point. + + Offset value + Index of indicator line + Offset start point + + + + Check if the point is a break point. + + Offset value + Index of indicator line + Offset start point + + + + Gets the price from historical data + + + + + + + + Get Bid price + + Offset value + + + + + Get Ask price + + Offset value + + + + + Get Last price + + Offset value + + + + + Get Open price + + Offset value + + + + + Get High price + + Offset value + + + + + Get Low price + + Offset value + + + + + Get Close price + + Offset value + + + + + Get Median price + + Offset value + + + + + Get Typical price + + Offset value + + + + + Get Weighted price + + Offset value + + + + + Get Volume + + Offset value + + + + + Get Volume in quoting asset + + Offset value + + + + + Get Ticks + + Offset value + + + + + Get Open interest + + Offset value + + + + + Get Funding rate + + Offset value + + + + + Get Time + + Offset value + + + + + Formatting price, using precision from assigned symbol or Digits value if specified + + Price value + + + + + Marks cloud begin between two line series with specific color + + First line series index + Second line series index + Cloud color + Offset + + + + Marks cloud end between two line series with specific color + + First line series index + Second line series index + Cloud color + Offset + + + + Using IndicatorLineMarker class you can mark by color or icon any point of your indicator's line. + + + + + Color of the marker + + + + + Icon that will be drawn above the indicator line + + + + + Icon that will be drawn beyond the indicator line + + + + + Constructor for IndicatorLineMarker + + + + + + + + Constructor for IndicatorLineMarker + + + + + Different type of icons for Indicator marker + + + + + Sets the bar number (from the data beginning) from which the drawing of the given indicator line must start; + + + + + Time shift of indicator's line + + + + + Redraws parts of indicator's line within the interval set by offset + + + + + Removes redrawn parts of indicator's line within the interval set by offset + + + + + + + + + + Fully clears markers from line + + + + + Use this attribute to mark input parameters of your script. You will see them in the settings screen on adding + + + + + Displayed name of input parameter + + + + + Sort index for input paramter + + + + + Minimal value for numeric input parameters + + + + + Maximal value for numeric input parameters + + + + + Increment value for numeric input parameters + + + + + Decimal palces for numeric input parameters + + + + + List of predefined values + + + + + Event occurred when write a new log + + + + + Get logs from the strategy for specified date range + + + + + + + + Write log message + + + + + + + The base class for strategies + + + + + Unique ID of the strategy + + + + + The current state of the strategy + + + + + Event occured when strategy write a new log + + + + + Event occured if any of strategy settings was changed + + + + + Run strategy + + + + + Stop strategy + + + + + Remove the strategy + + + + + Get current metrics from the strategy + + + + + + Get logs from the strategy for specified date range + + + + + + + + Write log message + + + + + + + Specifies constants that define which mouse button was pressed. + + + + + No mouse button was pressed. + + + + + The left mouse button was pressed. + + + + + The right mouse button was pressed. + + + + + The middle mouse button was pressed. + + + + + The first XButton was pressed. + + + + + The second XButton was pressed. + + + + + Gets or sets a value indicating whether the event was handled. + true to bypass the control's default handling; otherwise, false to also pass the event along to the default control handler. + + + + + Defines quote parameters for subscribtion + + + + + Symbol Id + + + + + Quote type + + + + + SubscribeQuotesParameters constructor + + + + + Id of the order + + + + + Gets or Sets the groups. + + + + + SettingItem cast type + + + + + SettingItem state + + + + + Specifies the DatePicker time format. + + + + + Date only + + + + + Date and time + + + + + Time only + + + + + Time with seconds + + + + + + + + + + Typecasts setting as TabControl item + + + + + Typecasts setting as GroupBox item + + + + + Typecasts setting as AccountLookup item + + + + + Typecasts setting as Button item + + + + + Typecasts setting as CheckBox item + + + + + Typecasts setting as CheckBox item + + + + + Typecasts setting as Color item + + + + + Typecasts setting as DateTimePicker item + + + + + Typecasts setting as NumericUpDown item + + + + + Typecasts setting as NumericUpDown item + + + + + Typecasts setting as AccountLookup item + + + + + Typecasts setting as Password item + + + + + Typecasts setting as Period item + + + + + Typecasts setting as ComboBox item + + + + + Typecasts setting as TextBox item + + + + + Typecasts setting as SymbolLookup item + + + + + Typecasts setting as TextBox item + + + + + Cancel all pending s + + the name of the initiator of the call (optional) + + + + Cancel all pending s by + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by and + + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by and + + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by , and + + + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by and + + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by , and + + + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by and + + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by , and + + + + + the name of the initiator of the call (optional) + + + + Cancel all pending s by + + + the name of the initiator of the call (optional) + + + + Close all s + + the name of the initiator of the call (optional) + List of + + + + Close all s by + + + the name of the initiator of the call (optional) + List of + + + + Close all s by + + + the name of the initiator of the call (optional) + List of + + + + Close all s by and + + + + the name of the initiator of the call (optional) + List of + + + + Close all s by + + + the name of the initiator of the call (optional) + List of + + + + Close all s by and + + + + the name of the initiator of the call (optional) + List of + + + + Close all positive s + + the name of the initiator of the call (optional) + List of + + + + Close all positive s by + + + the name of the initiator of the call (optional) + List of + + + + Close all negative s + + the name of the initiator of the call (optional) + List of + + + + Close all negative s by + + + the name of the initiator of the call (optional) + List of + + + + Reverse all s + + the name of the initiator of the call (optional) + List of + + + + Reverse all s by + + + the name of the initiator of the call (optional) + List of + + + + Reverse all s by + + + the name of the initiator of the call (optional) + List of + + + + Reverse by and + + + + the name of the initiator of the call (optional) + + + + + Cancel all s and close all s + + the name of the initiator of the call (optional) + + + + Cancel all s and close all s by + + + the name of the initiator of the call (optional) + + + + Cancel all s and close all s by and + + + + the name of the initiator of the call (optional) + + + + Modify SL to breakeven price + + + the name of the initiator of the call (optional) + + + + Modify SL to breakeven price with certain additional offset + + + offset in ticks + the name of the initiator of the call (optional) + + + + Modify SL to breakeven price + + + list of s that will be used for order placing + the name of the initiator of the call (optional) + + + + Adjust SL/TP for given + + + + the name of the initiator of the call (optional) + + + + Adjust stop loss for given + + + list of s + initial stop loss parameters (optional) + + the name of the initiator of the call (optional) + + + + + Adjust take profit for given + + + list of s + initial take profit parameters (optional) + + the name of the initiator of the call (optional) + + + + + The allowed result. + + + + + Gets the status. + + + + + Gets the reason. + + + + + Get the allowed result. + + An AllowedResult. + + + + Gets the not allowed result. + + The reason. + An AllowedResult. + + + + The action wrapper. + + + + + Gets or Sets the items. + + + + + Initializes a new instance of the class. + + + + + Froms the X element. + + The element. + The deserialization info. + + + + Tos the X element. + + A XElement. + + + + The aggressor flag calculator. + + + + + Initializes a new instance of the class. + + + + + Collect bid ask. + + The symbol. + The time ticks. + The bid. + The ask. + + + + Calculate aggressor flag. + + The symbol. + The time ticks. + The last. + An AggressorFlag. + + + + + + + + + Calculate aggressor flag. + + The previous bid. + The previous ask. + The last. + An AggressorFlag. + + + + The alert. + + + + + Gets or Sets the text. + + + + + Gets or Sets the symbol name. + + + + + Gets or Sets the connection name. + + + + + Gets or Sets the action on confirm. + + + + + Gets or Sets the name. + + + + + The alert data. + + + + + The name. + + + + + Gets or Sets the columns. + + + + + Gets or Sets the groups. + + + + + Gets or Sets the actions. + + + + + Gets or Sets a value indicating whether enabled. + + + + + Gets or Sets the table dictionary. + + + + + Gets or Sets a value indicating whether coloring is alert. + + + + + Gets or Sets a value indicating whether filter alert. + + + + + Gets or Sets a value indicating whether search alert. + + + + + Gets or Sets a value indicating whether to remove. + + + + + Gets or Sets the ID. + + + + + Gets or Sets the command sender. + + + + + Gets or Sets a value indicating whether need confirm trading. + + + + + Initializes a new instance of the class. + + + + + To the XML element. + + A XElement. + + + + From the XML element. + + The element. + The deserialization info. + + + + The arbitrage symbol. + + + + + Gets or Sets the index. + + + + + Gets the unique ID. + + + + + Gets the symbol id. + + + + + Gets or Sets a value indicating whether is selected. + + + + + Gets or Sets the symbol. + + + + + Gets or Sets the account. + + + + + Gets or Sets the commission. + + + + + Initializes a new instance of the class. + + + + + Compare to. + + The other. + An int. + + + + Froms the X element. + + The element. + The deserialization info. + + + + Tos the X element. + + A XElement. + + + + The action buffered processor. + + + + + The action buffered processor with priority. + + + + + Initializes a new instance of the class. + + The threads count. + + + + The buffered processor. + + + + + + Gets the state. + + + + + Gets the queue depth. + + + + + + + + + + + + + + + + + The subject. + + + + Wait all messages process. + + The external token. + + + + The buffered processor state. + + + + + + + + + + Агрегація, що буде використовуватись як дефолтна для "TimeFrameScreen". + Повинна відповідати агрегації чарта. + + + + + https://stackoverflow.com/questions/3060381/datetime-addmonths-adding-only-month-not-days + + Проблема: + (29 Feb).AddMonth(1) = 29 March + + + + + Defines advanced trading operation respond bundle + + + + + Respond operation state + + + + + Respond message + + + + + Respond value (optional) + + + + + Details of sub-operations + + + + + Get all available custom resources + + + + + Get custom resource stream by name + + + + + Check whether specified items was hidden by branding specification + + + + + + The assembly loader. + + + + + Load types. + + The folder name. + The target type. + The assembly name filter. + The search option. + If true, load in memory. + If true, allow loading references. + ]]> + + + + Quote type for subscribtion dictum + + + + + Level 1 quote + + + + + Level 2 quote + + + + + Last + + + + + Mark price + + + + + Trading operation state + + + + + Complex trading operation state + + + + + The aggressor flag calculator extensions. + + + + + Collect bid ask. + + The calculator. + The quote. + + + + Calculate aggressor flag. + + The calculator. + The last. + An AggressorFlag. + + + + Gets the price from historical data + + + + + + + + Get Bid price + + Offset value + + + + + Get Ask price + + Offset value + + + + + Get Last price + + Offset value + + + + + Get Open price + + Offset value + + + + + Get High price + + Offset value + + + + + Get Low price + + Offset value + + + + + Get Close price + + Offset value + + + + + Get Median price + + Offset value + + + + + Get Typical price + + Offset value + + + + + Get Weighted price + + Offset value + + + + + Get Volume + + Offset value + + + + + Get Volume in quoting asset + + Offset value + + + + + Get Ticks + + Offset value + + + + + Get Open interest + + Offset value + + + + + Get Funding rate + + Offset value + + + + + Get Time + + Offset value + + + + + Combines intersecting intervals + + + + + Get all settings or particular settings by provided hierarchy path + + + + + Write info log message + + + + + Write trading log message + + + + + Write error log message + + + + + Variable lifetime + + + + + The variable will be removed on exit + + + + + The variable will be written to file + + + + + Global variable entry + + + + + Variable name + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + List<GlobalVariable> global_List=new List<GlobalVariable>(); + + public override void Init() + { + if(GlobalVariablesManager.Count()>0) + { + global_List=GlobalVariablesManager.GetGlobalVariablesList(); + foreach (var el in global_List) + { + Print(el.Name); + Print(el.Value); + } + } + } + } + } + + + + + + + Variable value + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + List<GlobalVariable> global_List=new List<GlobalVariable>(); + public override void Init() + { + if(GlobalVariablesManager.Count()>0) + { + global_List=GlobalVariablesManager.GetGlobalVariablesList(); + foreach (var el in global_List) + { + Print(el.Name); + Print(el.Value); + } + } + } + } + } + + + + + + + Initializes new variable + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + List<GlobalVariable> global_List=new List<GlobalVariable>(); + public override void Init() + { + if(GlobalVariablesManager.Count()>0) + { + global_List=GlobalVariablesManager.GetGlobalVariablesList(); + foreach (var el in global_List) + { + //Simplified way to retrieve global variable value + el.GlobalVariable("new_global_variable_period", period) + + //However, to obtain certain variable, which belongs to indicator/strategy and to avoid unexpected erasing of data the best practice is to provide to a key holder multiple details such as name, params, hashed password etc. Follow SetValue() example. + } + } + } + } + } + + + + Variable name + Variable value + + + + Sets variable value to a global storage + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + GlobalVariablesManager(){ + base.ProjectName = "GlobalVariablesManager"; + base.Password=GetHashedPassword(ProjectName); + } + + [InputParameter("Period", 0, 1, 9999)] + public int period = 5; + + public override void OnQuote() + { + //Simplified way to store a global variable + + GlobalVariablesManager.SetValue("global_variable_period", period, VariableLifetime.SaveSession); + + //However, to indicate any variable belongs to certain indicator/strategy and to avoid unexpected erasing of data the best practice is to provide to a key holder multiple details such as name, params, hashed password etc. + + GlobalVariablesManager.SetValue("global_variable_period" +Symbols.Current.Name+period+Password, period, VariableLifetime.SaveSession); + } + } + } + + + + Variable name + New value + Variable lifetime + + + + Removes specified variable from global storage + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + public override void Init() + { + //Simplified way to remove a global variable + + GlobalVariablesManager.Remove("global_variable_period"); + + //However, to remove certain variable, which belongs to indicator/strategy and to avoid unexpected erasing of data the best practice is to provide to a key holder multiple details such as name, params, hashed password etc. Follow SetValue() example. + } + } + } + + + + Variable name + + + + Returns variables' count in global storage + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + public override void Init() + { + if(GlobalVariablesManager.Count()>0) + { + Print("Your session obtains "+GlobalVariablesManager.Count()+" global variables"); + } + } + } + } + + + + + + + + Removes all variables from global storage + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + public override void Init() + { + GlobalVariablesManager.RemoveAll(); + + if(GlobalVariablesManager.Count()==0) + { + Print("Your session does not have any global variables"); + } + } + } + } + + + + + + + Check if variable with specified name exists in global storage + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + public override void Init() + { + //Simplified way to check an existance of a global variable + + if(GlobalVariablesManager.Exists("global_variable_period")) + Print("Your session has this global variable"); + else + GlobalVariablesManager.SetValue("global_variable_period"); + + //However, to obtain certain variable, which belongs to indicator/strategy and to avoid unexpected erasing of data the best practice is to provide to a key holder multiple details such as name, params, hashed password etc. Follow SetValue() example. + } + } + } + + + + Variable name + + + + + Returns variable value by name + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + public override void Init() + { + //Simplified way to retrieve global variable value + + if(GlobalVariablesManager.Exists("global_variable_period")) + //Always perform a type casting before assigning any variable from global storage + period = (int)GlobalVariablesManager.GetValue("global_variable_period"); + + //However, to obtain certain variable, which belongs to indicator/strategy and to avoid unexpected erasing of data the best practice is to provide to a key holder multiple details such as name, params, hashed password etc. Follow SetValue() example. + } + } + } + + + + Variable name + Variable value + + + + Performs a variable assigning from a global storage if such name exists in a scope + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + public override void Init() + { + int new_period; + + //Simplified way to retrieve global variable value + + if(GlobalVariablesManager.TryGetValue("global_variable_period")) + Print("New variable is assigned from globals: " + new_period); + if(new_period==period) + Print("Matching, no need to re-assign globals: "); + else + GlobalVariablesManager.SetValue("global_variable_period", period, VariableLifetime.SaveSession); + + //However, to obtain certain variable, which belongs to indicator/strategy and to avoid unexpected erasing of data the best practice is to provide to a key holder multiple details such as name, params, hashed password etc. Follow SetValue() example. + } + } + } + + + + Variable name + Variable value + True if variable exists + + + + Saves all serializable variables to disk + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + Connection myConnection = Connection.CurrentConnection; + public override void OnQuote() + { + if(myConnection.Status==Disconnected) + GlobalVariablesManager.Flush(); + } + } + } + + + + + + + Returns all global variables as list + + + + + using System; + using System.Text; + using PTLRuntime.NETScript; + + namespace GlobalVariablesManager + { + public class GlobalVariablesManager : NETIndicator + { + List <GlobalVariable>global_List=new List<GlobalVariable>(); + public override void Init() + { + if(GlobalVariablesManager.Count()>0) + global_List=GlobalVariablesManager.GetGlobalVariablesList(); + + foreach (var el in global_List) + { + Print(el.Name); + } + } + } + } + + + + Collection of global variables + + + + Object is non-serializable + + + + + Object too big + + + + + The application logger event. + + + + + Current logging level + + + + + Sets logs with custom messag, logging level, connection name + + + + + Sets logs with exception and custom message (optional), logging level, connection name + + + + + Sets logs with objects inherited from ILoggable interface,logging level, connection name + + + + + Sets a log level in case of deep debug + + + + + Remove a log level if one has not needed already + + + + + Все параметры, которые могут использоваться при отправке email + + + + + Отправит сообщение по E-mail + + + + + + + + + + Чисто, як маркер для парсера xgettext.exe + + + + + + + Check, whether current translation equal to hidden + + + + + + + + + + + + + + + + + + + + + + + Allows to save Symbols into named lists + + + + + from: + http://www.codeguru.com/csharp/csharp/cs_date_time/timeroutines/article.php/c4207/C-SNTP-Client.htm + +++ добавлен диспозе, подправлен ToString(), убран лишний метод. + +++ ReceiveTimeoutбSendTimeout + + NTPClient is a C# class designed to connect to time servers on the Internet. + The implementation of the protocol is based on the RFC 2030. + + Public class members: + + LeapIndicator - Warns of an impending leap second to be inserted/deleted in the last + minute of the current day. (See the _LeapIndicator enum) + + VersionNumber - Version number of the protocol (3 or 4). + + Mode - Returns mode. (See the _Mode enum) + + Stratum - Stratum of the clock. (See the _Stratum enum) + + PollInterval - Maximum interval between successive messages. + + Precision - Precision of the clock. + + RootDelay - Round trip time to the primary reference source. + + RootDispersion - Nominal error relative to the primary reference source. + + ReferenceTimestamp - The time at which the clock was last set or corrected. + + OriginateTimestamp - The time at which the request departed the client for the server. + + ReceiveTimestamp - The time at which the request arrived at the server. + + Transmit Timestamp - The time at which the reply departed the server for client. + + RoundTripDelay - The time between the departure of request and arrival of reply. + + LocalClockOffset - The offset of the local clock relative to the primary reference + source. + + Initialize - Sets up data structure and prepares for connection. + + Connect - Connects to the time server and populates the data structure. + + IsResponseValid - Returns true if received data is valid and if comes from + a NTP-compliant time server. + + ToString - Returns a string representation of the object. + + ----------------------------------------------------------------------------- + Structure of the standard NTP header (as described in RFC 2030) + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |LI | VN |Mode | Stratum | Poll | Precision | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Root Delay | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Root Dispersion | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reference Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Reference Timestamp (64) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Originate Timestamp (64) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Receive Timestamp (64) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Transmit Timestamp (64) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Key Identifier (optional) (32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | | + | Message Digest (optional) (128) | + | | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + ----------------------------------------------------------------------------- + + NTP Timestamp Format (as described in RFC 2030) + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Seconds | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Seconds Fraction (0-padded) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + + + Connect to the time server + + + + + Defines a trading operation respond bundle + + + + + Respond operation state + + + + + Respond message + + + + + Respond order id + + + + + Provides VA calculation parameters + + + + + Provides VA calculation request per + + + + + Summary calculated Volume info + + + + + Volume info for each price + + + + + Fire in case of price level was added or existing was updated + + + + + Represent item with Volume Analysis calculation results + + + + + Volume Analysis calculations + + + + + Calculate volume profile for requested time range + + + + + Calculate volume profile for each bar in History Data + + + + + Calculate volume profile for requested time range + + + + + Calculate volume profile for each bar in History Data + + + + + + + + + + + + + + diff --git a/.github/workflows/main_automation.yml b/.github/workflows/main_automation.yml index 2130d485..e45f7eb5 100644 --- a/.github/workflows/main_automation.yml +++ b/.github/workflows/main_automation.yml @@ -3,10 +3,10 @@ on: workflow_dispatch: push: branches: - - '*' + - main pull_request: branches: - - '*' + - main jobs: build_test: @@ -45,17 +45,6 @@ jobs: #configFilePath: GitVersion.yml updateAssemblyInfo: true -############## Install Python - - - name: Install pandas-ta - run: | - sudo apt install python3.10 - sudo apt install python3.10-dev - pip3 install numpy - pip3 install pandas - pip3 install pandas-ta - python --version - ############## Install more tools - name: Install JDK11 for Sonar Scanner @@ -84,21 +73,23 @@ jobs: - name: Build Main branch of QuanTAlib DLL if: ${{ github.ref != 'refs/heads/dev' }} - run: dotnet build ./Calculations/Calculations.csproj --configuration Release --nologo -p:PackageVersion=${{ steps.gitversion.outputs.MajorMinorPatch }} + run: dotnet build ./lib/quantalib.csproj --configuration Release --nologo -p:PackageVersion=${{ steps.gitversion.outputs.MajorMinorPatch }} - name: Build dev branch of QuanTAlib DLL if: ${{ github.ref == 'refs/heads/dev' }} - run: dotnet build ./Calculations/Calculations.csproj --configuration Release --nologo -p:PackageVersion=${{ steps.gitversion.outputs.FullSemVer }} - - name: Build Indicators DLL - run: dotnet build ./Indicators/Indicators.csproj --configuration Release --nologo - - name: Build Strategies DLL - run: dotnet build ./Strategies/Strategies.csproj --configuration Release --nologo + run: dotnet build ./lib/quantalib.csproj --configuration Release --nologo -p:PackageVersion=${{ steps.gitversion.outputs.FullSemVer }} + - name: Build Averages DLL + run: dotnet build ./quantower/Averages/Averages.csproj --configuration Release --nologo + - name: Build Statistics DLL + run: dotnet build ./quantower/Statistics/Statistics.csproj --configuration Release --nologo + - name: Build SyntheticVendor DLL + run: dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo - name: DotCover Test HTML if: ${{ github.ref == 'refs/heads/dev' }} - run: dotnet dotcover test Tests/Tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html + run: dotnet dotcover test tests/tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html - name: DotCover Test XML if: ${{ github.ref == 'refs/heads/dev' }} - run: dotnet dotcover test Tests/Tests.csproj --dcReportType=DetailedXML --dcoutput=./dotcover.xml --verbosity=Detailed + run: dotnet dotcover test tests/tests.csproj --dcReportType=DetailedXML --dcoutput=./dotcover.xml --verbosity=Detailed - name: Coverlet Test if: ${{ github.ref == 'refs/heads/dev' }} run: dotnet test -p:CollectCoverage=true --collect:"XPlat Code Coverage" --results-directory "./" @@ -145,12 +136,13 @@ jobs: release_name: ${{ steps.gitversion.outputs.SemVer }} tag_name: prerelease release_config: | - Calculations/bin/Release/net6.0/QuanTAlib.dll - Indicators/bin/Release/QuanTAlib_Indicators.dll - Strategies/bin/Release/QuanTAlib_Strategies.dll + lib/bin/Release/QuanTAlib.dll + quantower/Averages/bin/Release/Averages.dll + quantower/Statistics/bin/Release/Statistics.dll + SyntheticVendor/bin/Release/SyntheticVendor.dll - name: Push package to myget.org - run: dotnet nuget push 'Calculations/bin/Release/QuanTAlib.*.nupkg' + run: dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' --api-key ${{ secrets.MYGET_DEPLOY_KEY_QUANTALIB }} --source https://www.myget.org/F/quantalib/api/v2/package --skip-duplicate @@ -168,13 +160,14 @@ jobs: release_name: ${{ steps.gitversion.outputs.MajorMinorPatch }} tag_name: latest release_config: | - Calculations/bin/Release/net6.0/QuanTAlib.dll - Indicators/bin/Release/QuanTAlib_Indicators.dll - Strategies/bin/Release/QuanTAlib_Strategies.dll + lib/bin/Release/QuanTAlib.dll + quantower/Averages/bin/Release/Averages.dll + quantower/Statistics/bin/Release/Statistics.dll + SyntheticVendor/bin/Release/SyntheticVendor.dll - name: Push package to nuget.org if: ${{ github.ref == 'refs/heads/main' }} - run: dotnet nuget push 'Calculations/bin/Release/QuanTAlib.*.nupkg' + run: dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' --api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }} --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 00000000..93975f32 --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,30 @@ +name: SonarCloud analysis +on: + push: + pull_request: + workflow_dispatch: + +permissions: + pull-requests: read # allows SonarCloud to decorate PRs with analysis results + +jobs: + Analysis: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + + - name: Analyze with SonarCloud + uses: SonarSource/sonarcloud-github-action@v2.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN + with: + # Additional arguments for the SonarScanner CLI + args: > + -Dsonar.projectKey=mihakralj_QuanTAlib + -Dsonar.organization=mihakralj + -Dsonar.sources=. + -Dsonar.verbose=false diff --git a/.gitignore b/.gitignore index 9413e591..dc4496da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -9,12 +9,6 @@ *.user *.userosscache *.sln.docstates -.vscode/ -.fleet/ -*.deps.json -.Sandbox/ -#.sonarlint/ -.DS_Store # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -29,6 +23,7 @@ mono_crash.* [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ @@ -67,6 +62,9 @@ project.lock.json project.fragment.lock.json artifacts/ +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -92,6 +90,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -143,6 +142,11 @@ _TeamCity* .axoCover/* !.axoCover/settings.json +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml @@ -290,6 +294,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -346,6 +361,9 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database healthchecksdb @@ -354,5 +372,27 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ -dotCover.Output.dcvr -/Tests/GlobalSuppressions.cs + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml \ No newline at end of file diff --git a/.refactoring/base.cs b/.refactoring/base.cs deleted file mode 100644 index e3e84b86..00000000 --- a/.refactoring/base.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; - -public struct TValue -{ - public DateTime Timestamp { get; set; } - public double Value { get; set; } - - public TValue(DateTime timestamp, double value) - { - Timestamp = timestamp; - Value = value; - } - - public override string ToString() - { - return $"[{this.Timestamp:yyyy-MM-dd HH:mm:ss}: {this.Value:F2}]"; - } - public override bool Equals(object obj) - { - if (obj is TValue other) - { - return Timestamp == other.Timestamp && Value == other.Value; - } - return false; - } - - public override int GetHashCode() - { - return HashCode.Combine(Timestamp, Value); - } -} - - -public struct TBar -{ - public DateTime Timestamp { get; set; } - public double Open { get; set; } - public double High { get; set; } - public double Low { get; set; } - public double Close { get; set; } - public double Volume { get; set; } - - public TBar(DateTime timestamp, double open, double high, double low, double close, double volume) - { - Timestamp = timestamp; - Open = open; - High = high; - Low = low; - Close = close; - Volume = volume; - } - - public override string ToString() - { - return $"[{this.Timestamp:yyyy-MM-dd HH:mm:ss}: O={this.Open:F2}, H={this.High:F2}, L={this.Low:F2}, C={this.Close:F2}, V={this.Volume:F2}]"; - } - - public override bool Equals(object obj) - { - if (obj is TBar other) - { - return Timestamp == other.Timestamp && - Open == other.Open && - High == other.High && - Low == other.Low && - Close == other.Close && - Volume == other.Volume; - } - return false; - } - - public override int GetHashCode() - { - return HashCode.Combine(Timestamp, Open, High, Low, Close, Volume); - } -} - - - -public class TValueEventArg : EventArgs -{ - public T Data { get; } - public bool IsClosed { get; } - public bool IsHot { get; } - - public TValueEventArg(T data, bool isClosed, bool isHot) - { - Data = data; - IsClosed = isClosed; - IsHot = isHot; - } -} \ No newline at end of file diff --git a/.refactoring/test.dib b/.refactoring/test.dib deleted file mode 100644 index 6c442688..00000000 --- a/.refactoring/test.dib +++ /dev/null @@ -1,92 +0,0 @@ -#!meta - -{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} - -#!csharp - -#load "./base.cs" - -#!csharp - -TValue vv = new(DateTime.Now, 100); -display(vv.ToString()); - -#!csharp - -TBar bb = new(); -display(bb.ToString()); - -#!csharp - -public class Emitter { - private Random random = new Random(); - public event EventHandler> Pub; - public void Emit() { - DateTime now = DateTime.Now; - double randomValue = random.NextDouble() * 100; // Generates a random number between 0 and 100 - TValue value = new TValue(now, randomValue); - - TValueEventArg eventArg = new TValueEventArg(value, true, true); - OnValuePub(eventArg); - } - protected virtual void OnValuePub(TValueEventArg eventArg) { - Pub?.Invoke(this, eventArg); - } -} - -public class BarEmitter -{ - private Random random = new Random(); - public event EventHandler> Pub; - private double lastClose = 100.0; // Starting price - - public void Emit() - { - double open = lastClose; - double close = open * (1 + (random.NextDouble() - 0.5) * 0.02); // +/- 1% change - double high = Math.Max(open, close) * (1 + random.NextDouble() * 0.005); // Up to 0.5% higher - double low = Math.Min(open, close) * (1 - random.NextDouble() * 0.005); // Up to 0.5% lower - double volume = random.NextDouble() * 1000000; // Random volume between 0 and 1,000,000 - - TBar bar = new TBar(DateTime.Now, open, high, low, close, volume); - lastClose = close; - - TValueEventArg eventArg = new TValueEventArg(bar, true, true); - OnBarPub(eventArg); - } - - protected virtual void OnBarPub(TValueEventArg eventArg) - { - Pub?.Invoke(this, eventArg); - } -} - - -public class Listener -{ - public void Sub(object sender, EventArgs e) - { - if (e is TValueEventArg tValueArg) { - Console.WriteLine($"TValue: {tValueArg.Data.Value:F2}"); - } else if (e is TValueEventArg tBarArg) { - Console.WriteLine($"TBar: o={tBarArg.Data.Open:F2}, v={tBarArg.Data.Volume:F2}"); - } else { - Console.WriteLine($"Unknown type: {e.GetType().Name}"); - } - } -} - -#!csharp - -Emitter em1 = new(); -BarEmitter em2 = new(); -Listener list = new(); - -em1.Pub += list.Sub; -em2.Pub += list.Sub; - -// Emit 5 random values -for (int i = 0; i < 3; i++) { - em1.Emit(); - em2.Emit(); -} diff --git a/Calculations/Basics/ADD_Series.cs b/Calculations/Basics/ADD_Series.cs deleted file mode 100644 index 0588d216..00000000 --- a/Calculations/Basics/ADD_Series.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -ADD - adding TSeries+TSeries together, or TSeries+double, or double+TSeries - -Remarks: - Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator. - - */ - -public class ADD_Series : Pair_TSeries_Indicator -{ - public ADD_Series(TSeries d1, TSeries d2 ) : base(d1, d2) { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public ADD_Series(TSeries d1, double dd2 ) : base(d1, dd2) { - if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public ADD_Series(double dd1, TSeries d2 ) : base(dd1, d2) { - if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, TValue1.v+TValue2.v); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} diff --git a/Calculations/Basics/CORR_Series.cs b/Calculations/Basics/CORR_Series.cs deleted file mode 100644 index f82d82de..00000000 --- a/Calculations/Basics/CORR_Series.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -CORR: Pearson's Correlation Coefficient - PCC is a measure of linear correlation between two sets of data. - It is the ratio between the covariance of two variables and the product of - their standard deviations; it is essentially a normalized measurement of - the covariance, such that the result always has a value between −1 and 1. - -Sources: - https://en.wikipedia.org/wiki/Pearson_correlation_coefficient - - */ - -public class CORR_Series : Pair_TSeries_Indicator -{ - public CORR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - - private readonly System.Collections.Generic.List _x = new(); - private readonly System.Collections.Generic.List _xx = new(); - private readonly System.Collections.Generic.List _y = new(); - private readonly System.Collections.Generic.List _yy = new(); - private readonly System.Collections.Generic.List _xy = new(); - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - Add_Replace_Trim(_x, TValue1.v, _p, update); - Add_Replace_Trim(_xx, TValue1.v * TValue1.v, _p, update); - Add_Replace_Trim(_y, TValue2.v, _p, update); - Add_Replace_Trim(_yy, TValue2.v * TValue2.v, _p, update); - Add_Replace_Trim(_xy, TValue1.v * TValue2.v, _p, update); - - double _sumx = _x.Sum(); - double _sumxx = _xx.Sum(); - double _sumy = _y.Sum(); - double _sumyy = _yy.Sum(); - double _sumxy = _xy.Sum(); - - double _covar = (_sumxx - _sumx * _sumx / _p) * (_sumyy - _sumy * _sumy / _p); - double _cor = (_covar != 0) ? (_sumxy - _sumx * _sumy / _p) / Math.Sqrt(_covar) : 0.0; - - var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _cor); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - - } -} diff --git a/Calculations/Basics/COVAR_Series.cs b/Calculations/Basics/COVAR_Series.cs deleted file mode 100644 index 4d6891c6..00000000 --- a/Calculations/Basics/COVAR_Series.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -COVAR: Covariance - Covariance is defined as the expected value (or mean) of the product - of their deviations from their individual expected values. - -Sources: - https://en.wikipedia.org/wiki/Covariance - - */ - - -public class COVAR_Series : Pair_TSeries_Indicator -{ - public COVAR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN) - { - if (base._d1.Count > 0 && base._d2.Count > 0) { - for (int i = 0; i < base._d1.Count; i++) { - this.Add(base._d1[i], base._d2[i], false); - } - } - } - - private readonly System.Collections.Generic.List _x = new(); - private readonly System.Collections.Generic.List _y = new(); - private readonly System.Collections.Generic.List _xy = new(); - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) - { - BufferTrim(_x, TValue1.v, _p, update); - BufferTrim(_y, TValue2.v, _p, update); - BufferTrim(_xy, TValue1.v * TValue2.v, _p, update); - - double _avgx = _x.Average(); - double _avgy = _y.Average(); - double _avgxy = _xy.Average(); - double _covar = _avgxy - (_avgx * _avgy); - - var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _covar); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} diff --git a/Calculations/Basics/DIV_Series.cs b/Calculations/Basics/DIV_Series.cs deleted file mode 100644 index 16069917..00000000 --- a/Calculations/Basics/DIV_Series.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -DIV - divide TSeries/TSeries , or TSeries/double, or double/TSeries - -Remarks: - Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator. - */ - -public class DIV_Series : Pair_TSeries_Indicator -{ - public DIV_Series(TSeries d1, TSeries d2 ) : base(d1, d2) { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public DIV_Series(TSeries d1, double dd2 ) : base(d1, dd2) { - if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public DIV_Series(double dd1, TSeries d2 ) : base(dd1, d2) { - if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, - (TValue2.v is not 0) ? TValue1.v/TValue2.v : Double.PositiveInfinity); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} \ No newline at end of file diff --git a/Calculations/Basics/MUL_Series.cs b/Calculations/Basics/MUL_Series.cs deleted file mode 100644 index c1c573bd..00000000 --- a/Calculations/Basics/MUL_Series.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -MUL - multiply TSeries*TSeries together, or TSeries*double, or double*TSeries - - */ - -public class MUL_Series : Pair_TSeries_Indicator -{ - public MUL_Series(TSeries d1, TSeries d2 ) : base(d1, d2) { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public MUL_Series(TSeries d1, double dd2 ) : base(d1, dd2) { - if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public MUL_Series(double dd1, TSeries d2 ) : base(dd1, d2) { - if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, - TValue1.v*TValue2.v); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} \ No newline at end of file diff --git a/Calculations/Basics/SUB_Series.cs b/Calculations/Basics/SUB_Series.cs deleted file mode 100644 index 88511f81..00000000 --- a/Calculations/Basics/SUB_Series.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -SUB - subtracting TSeries-TSeries, or TSeries-double, or double-TSeries - - */ - - -public class SUB_Series : Pair_TSeries_Indicator -{ - public SUB_Series(TSeries d1, TSeries d2 ) : base(d1, d2) { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public SUB_Series(TSeries d1, double dd2 ) : base(d1, dd2) { - if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public SUB_Series(double dd1, TSeries d2 ) : base(dd1, d2) { - if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update) - { - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, - TValue1.v-TValue2.v); - if (update) { base[base.Count - 1] = result; } else { base.Add(result); } - } -} \ No newline at end of file diff --git a/Calculations/ClassStructures/Pair_TSeries_Abstract.cs b/Calculations/ClassStructures/Pair_TSeries_Abstract.cs deleted file mode 100644 index 99ccfee6..00000000 --- a/Calculations/ClassStructures/Pair_TSeries_Abstract.cs +++ /dev/null @@ -1,132 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -Abstract classes with all scaffolding required to build indicators. - All abstracts support period, NaN, and all permutations of Add() methods. - Indicator classess need to implement: - - Chaining constructor (Abstract's constructor executes first) - - Default Add(value) class - - optional Add(series) bulk insert class (for optimization of historical analysis) - - Single_TSeries_Indicator - one single-value TSeries in, one TSeries out. - Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring) - Single_TBars_Indicator - One OHLCV TBars in, one TSeries out. - - */ - -public abstract class Pair_TSeries_Indicator : TSeries { - protected readonly int _p; - protected readonly bool _NaN; - protected readonly TSeries _d1; - protected readonly TSeries _d2; - protected readonly double _dd1, _dd2; - - // Chainable Constructors - add them at the end of primary constructors if needed - protected Pair_TSeries_Indicator(TSeries source1, TSeries source2, int period, bool useNaN) { - _p = period; - _NaN = useNaN; - _d1 = source1; - _d2 = source2; - _dd1 = double.NaN; - _dd2 = double.NaN; - _d1.Pub += Sub; - _d2.Pub += Sub; - } - - protected Pair_TSeries_Indicator(TSeries source1, TSeries source2) { - _d1 = source1; - _d2 = source2; - _dd1 = double.NaN; - _dd2 = double.NaN; - _d1.Pub += Sub; - _d2.Pub += Sub; - } - - protected Pair_TSeries_Indicator(TSeries source1, double dd2) { - _d1 = source1; - _d2 = new TSeries(); - _dd1 = double.NaN; - _dd2 = dd2; - _d1.Pub += Sub; - } - - protected Pair_TSeries_Indicator(double dd1, TSeries source2) { - _d1 = new TSeries(); - _d2 = source2; - _dd1 = dd1; - _dd2 = double.NaN; - _d2.Pub += Sub; - } - - // overridable Add(Tvalue, Tvalue) method to add/update a single value at the end of the list - public virtual void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2, bool update) { - base.Add((TValue1.t, 0), update); - // default inserts zeros - } - - // potentially overridable Add() bulk variations (could be replaced with faster bulk algos) - public virtual void Add(TSeries d1, TSeries d2) { - for (var i = 0; i < d1.Count; i++) { - Add(d1[i], d2[i], false); - } - } - - public virtual void Add(TSeries d1, double dd2) { - for (var i = 0; i < d1.Count; i++) { - Add(d1[i], (d1[i].t, dd2), false); - } - } - - public virtual void Add(double dd1, TSeries d2) { - for (var i = 0; i < d2.Count; i++) { - Add((d2[i].t, dd1), d2[i], false); - } - } - - public void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2) { - Add(TValue1, TValue2, false); - } - - public void Add(bool update) { - if (_dd1 is double.NaN && _dd2 is double.NaN) { - // (Series, Series) - if (update || (_d1.Count > Count && _d2.Count > Count)) { - Add(_d1[_d1.Count - 1], _d2[_d2.Count - 1], update); - } - } - else if (_dd2 is not double.NaN && _dd1 is double.NaN) { - // (Series, Double) - Add(_d1[_d1.Count - 1], (_d1[_d1.Count - 1].t, _dd2), update); - } - else { - // (Double, Series) - Add((_d2[_d2.Count - 1].t, _dd1), _d2[_d2.Count - 1], update); - } - } - - public void Add() { - Add(false); - } - - public new void Sub(object source, TSeriesEventArgs e) { - Add(e.update); - } - - protected static void Add_Replace(List l, double v, bool update) { - if (update) { - l[l.Count - 1] = v; - } - else { - l.Add(v); - } - } - - protected static void Add_Replace_Trim(List l, double v, int p, bool update) { - Add_Replace(l, v, update); - if (l.Count > p && p != 0) { - l.RemoveAt(0); - } - } -} diff --git a/Calculations/Logic/COMPARE_Series.cs b/Calculations/Logic/COMPARE_Series.cs deleted file mode 100644 index 4ee84c96..00000000 --- a/Calculations/Logic/COMPARE_Series.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -COMPARE - Generates +1 if A is above B, -1 if A is below B and 0 if A=B - - - */ - -public class COMPARE_Series : Pair_TSeries_Indicator { - - public COMPARE_Series(TSeries d1, TSeries d2) : base(d1, d2) { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public COMPARE_Series(TSeries d1, double dd2) : base(d1, dd2) { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public COMPARE_Series(double dd1, TSeries d2) : base(dd1, d2) { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) { - - double val = TValue1.v > TValue2.v ? 1 : -1; - val = TValue1.v == TValue2.v ? 0 : val; - (System.DateTime t, double v) over = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, TValue1.v > TValue2.v ? 1 : val); - if (update) { base[^1] = over; } - else { base.Add(over); } - - - } -} - - diff --git a/Calculations/Logic/CROSS_Series.cs b/Calculations/Logic/CROSS_Series.cs deleted file mode 100644 index 2c4cad1c..00000000 --- a/Calculations/Logic/CROSS_Series.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -OVER - Generates +1 if A is above B, -1 if A is below B and 0 if A=B - -Remarks: - OVER.Cross generates 1 when A breaks B from below and -1 when A breaks B from above - - */ - -public class CROSS_Series : Pair_TSeries_Indicator { - public TSeries Cross { get; set; } = new(); - - private double _previous = double.NaN; - public CROSS_Series(TSeries d1, TSeries d2) : base(d1, d2) { - if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } - } - public CROSS_Series(TSeries d1, double dd2) : base(d1, dd2) { - if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } - } - public CROSS_Series(double dd1, TSeries d2) : base(dd1, d2) { - if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } - } - - public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) { - - double val = TValue1.v > TValue2.v ? 1 : -1; - val = TValue1.v == TValue2.v ? 0 : val; - double over = TValue1.v > TValue2.v ? 1 : val; - - val = (_previous < over) ? 1 : -1; - val = ((_previous == over) || Double.IsNaN(this._previous) || (this._previous == 0)) ? 0 : val; - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t,val); - - this._previous = over; - - if (update) { base[^1] = result; } - else { base.Add(result); } - - } -} - - diff --git a/Calculations/Logic/TOrders.cs b/Calculations/Logic/TOrders.cs deleted file mode 100644 index 04d5ffa3..00000000 --- a/Calculations/Logic/TOrders.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data; -using System.Linq; - - -public enum OType { - NIL = 0, // No position - BTO = 1, // Buy to Open - STC = 2, // Sell to Close - STO = 3, // Sell to Open - BTC = 4, // Buy to Close - END = 5, // Exit the trade -} - - -public class TOrders : List<(DateTime t, OType o)> { - - public void Add((DateTime t, OType o) TOrder, bool update = false) - { - if (update) { this[^1] = TOrder; } - else { base.Add(TOrder); } - OnEvent(update); - } - - - protected virtual void OnEvent(bool update = false) { - Pub?.Invoke(this, new TSeriesEventArgs { update = update }); } - public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); - public event NewDataEventHandler Pub; - -} \ No newline at end of file diff --git a/Calculations/_Updated/ADL_Series.cs b/Calculations/_Updated/ADL_Series.cs deleted file mode 100644 index 2222887e..00000000 --- a/Calculations/_Updated/ADL_Series.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ADL: Chaikin Accumulation/Distribution Line - ADL is a volume-based indicator that measures the cumulative Money Flow Volume: - - 1. Money Flow Multiplier = [(Close - Low) - (High - Close)] /(High - Low) - 2. Money Flow Volume = Money Flow Multiplier x Volume for the Period - 3. ADL = Previous ADL + Current Period's Money Flow Volume - -Sources: - https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line - - */ - -public class ADL_Series : TSeries { - protected readonly TBars _data; - private double _lastadl, _lastlastadl; - - //core constructors - public ADL_Series() { - Name = $"ADL()"; - _lastadl = _lastlastadl = 0; - } - public ADL_Series(TBars source) { - _data = source; - Name = $"ADL({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _lastadl = _lastlastadl = 0; - _data.Pub += Sub; - Add(data: _data); - } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - if (update) { this._lastadl = this._lastlastadl; } - else { this._lastlastadl = this._lastadl; } - - double _adl = 0; - double tmp = TBar.h - TBar.l; - if (tmp > 0.0) { - _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); - } - _lastadl = _adl; - - var ret = (TBar.t, _adl); - return base.Add(ret, update); - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _lastadl = _lastlastadl = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ADOSC_Series.cs b/Calculations/_Updated/ADOSC_Series.cs deleted file mode 100644 index 4c943b07..00000000 --- a/Calculations/_Updated/ADOSC_Series.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ADOSC: Chaikin Accumulation/Distribution Oscillator - ADO measures the momentum of ADL using the difference between slow (10-day) EMA(ADL) - and fast (3-day) EMA(ADL): - - Chaikin A/D Oscillator is defined as 3-day EMA of ADL minus 10-day EMA of ADL - -Sources: - https://school.stockcharts.com/doku.php?id=technical_indicators:chaikin_oscillator - - */ - -public class ADOSC_Series : TSeries { - protected readonly TBars _data; - private readonly double _k1, _k2; - private double _lastema1, _lastlastema1, _lastema2, _lastlastema2; - private double _lastadl, _lastlastadl; - - //core constructors - public ADOSC_Series(int shortPeriod, int longPeriod, bool useNaN = false) { - Name = $"ADOSC()"; - _k1 = 2.0 / (shortPeriod + 1); - _k2 = 2.0 / (longPeriod + 1); - _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; - } - public ADOSC_Series(TBars source, int shortPeriod, int longPeriod, bool useNaN = false) :this(shortPeriod, longPeriod, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _lastadl = _lastlastadl = 0; - _data.Pub += Sub; - Add(data: _data); - } - - public ADOSC_Series() : this(shortPeriod: 3, longPeriod: 10, useNaN: false) {} - - public ADOSC_Series(TBars source) : this(source, shortPeriod: 3, longPeriod:10, useNaN:false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update= false) { - - if (update) { - _lastadl = _lastlastadl; - _lastema1 = _lastlastema1; - _lastema2 = _lastlastema2; - } - - double _adl = 0; - double tmp = TBar.h - TBar.l; - if (tmp > 0.0) { _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); } - if (this.Count == 0) { _lastema1 = _lastema2 = _adl; } - - double _ema1 = (_adl - _lastema1) * _k1 + _lastema1; - double _ema2 = (_adl - _lastema2) * _k2 + _lastema2; - - _lastlastadl = _lastadl; - _lastadl = _adl; - _lastlastema1 = _lastema1; - _lastema1 = _ema1; - _lastlastema2 = _lastema2; - _lastema2 = _ema2; - - double _adosc = _ema1 - _ema2; - - var ret = (TBar.t, _adosc); - return base.Add(ret, update); - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ALMA_Series.cs b/Calculations/_Updated/ALMA_Series.cs deleted file mode 100644 index e4c8c3de..00000000 --- a/Calculations/_Updated/ALMA_Series.cs +++ /dev/null @@ -1,114 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -ALMA: Arnaud Legoux Moving Average - The ALMA moving average uses the curve of the Normal (Gauss) distribution, which - can be shifted from 0 to 1. This allows regulating the smoothness and high - sensitivity of the indicator. Sigma is another parameter that is responsible for - the shape of the curve coefficients. This moving average reduces lag of the data - in conjunction with smoothing to reduce noise. - - -Sources: - https://phemex.com/academy/what-is-arnaud-legoux-moving-averages - https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/ - - Discrepancy with Pandas-TA (but passes the validation with Skender.GetAlma) - */ - -public class ALMA_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - private readonly System.Collections.Generic.List _buffer = new(); - private readonly System.Collections.Generic.List _weight; - private double _norm; - private readonly double _offset, _sigma; - - //core constructors - public ALMA_Series(int period, double offset, double sigma, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"ALMA({period})"; - _offset = offset; - _sigma = sigma; - _weight = new(); - } - public ALMA_Series(TSeries source, int period, double offset, double sigma, bool useNaN) : this(period, offset, sigma, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - public ALMA_Series() : this(period:0, offset:0.85, sigma:6.0, useNaN: false) { } - public ALMA_Series(int period) : this(period: period, offset:0.85, sigma:6.0, useNaN:false) { } - public ALMA_Series(TBars source) : this(source:source.Close, period:0, offset:0.85, sigma:6.0, useNaN:false) { } - public ALMA_Series(TBars source, int period) : this(source:source.Close, period:period, offset: 0.85, sigma: 6.0, useNaN: false) { } - public ALMA_Series(TBars source, int period, double offset, double sigma, bool useNaN) : this(source.Close, period:period, offset: offset, sigma: sigma, useNaN: false) { } - public ALMA_Series(TSeries source) : this(source, period:0, offset:0.85, sigma:6.0, useNaN:false) { } - public ALMA_Series(TSeries source, int period) : this(source:source, period:period, offset:0.85, sigma:6.0, useNaN:false) { } - public ALMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: useNaN) { } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, double.NaN), update); - } - - BufferTrim(_buffer, TValue.v, _period, update); - if (_weight.Count < _buffer.Count) { - for (var i = 0; i < _buffer.Count - _weight.Count; i++) { - _weight.Add(0.0); - } - } - - - if (_buffer.Count <= _period || _period == 0) { - var _len = _buffer.Count; - _norm = 0; - var _m = _offset * (_len - 1); - var _s = _len / _sigma; - for (var i = 0; i < _len; i++) { - var _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s)); - _weight[i] = _wt; - _norm += _wt; - } - } - - double _weightedSum = 0; - for (var i = 0; i < _buffer.Count; i++) { - _weightedSum += _weight[i] * _buffer[i]; - } - - var _alma = _weightedSum / _norm; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - //reset calculation - public override void Reset() { - _buffer.Clear(); - _weight.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ATRP_Series.cs b/Calculations/_Updated/ATRP_Series.cs deleted file mode 100644 index 7f4cf4c7..00000000 --- a/Calculations/_Updated/ATRP_Series.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ATRP: Average True Range Percent - Average True Range Percent is (ATR/Close Price)*100. - This normalizes so it can be compared to other stocks. - -Sources: - https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atrp - - */ - -public class ATRP_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private double _k; - private int _len; - private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; - - //core constructors - public ATRP_Series(int period, bool useNaN) { - _period = period; - _k = 1.0 / (double)(_period); - _NaN = useNaN; - _len = 0; - Name = $"ATRP({period})"; - } - public ATRP_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public ATRP_Series() : this(period: 1, useNaN: false) { } - public ATRP_Series(int period) : this(period: period, useNaN: false) { } - public ATRP_Series(TBars source) : this(source, period: 1, useNaN: false) { } - public ATRP_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } - else { - _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; - _k = (_period == 0) ? 1 / (double)_len : _k; - _len++; - } - - if (_len == 1) { _cm1 = TBar.c; } - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - _cm1 = TBar.c; - - double _atr = 0; - if (this.Count == 0) { _atr = d.v; } - else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } - else { _atr = _k * (d.v - _lastatr) + _lastatr; } - _lastatr = _atr; - double _atrp = 100 * (_atr / TBar.c); - - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atrp); - return base.Add(res, update); - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ATR_Series.cs b/Calculations/_Updated/ATR_Series.cs deleted file mode 100644 index be22668d..00000000 --- a/Calculations/_Updated/ATR_Series.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ATR: wildeR Moving Average - The average true range (ATR) is a price volatility indicator - showing the average price variation of assets within a given time period. - -Sources: - https://en.wikipedia.org/wiki/Average_true_range - https://www.tradingview.com/wiki/Average_True_Range_(ATR) - https://www.investopedia.com/terms/a/atr.asp - - */ - -public class ATR_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private double _k; - private int _len; - private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; - - //core constructors - public ATR_Series(int period, bool useNaN) { - _period = period; - _k = 1.0 / (double)(_period); - _NaN = useNaN; - _len = 0; - Name = $"ATR({period})"; - } - public ATR_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public ATR_Series() : this(period: 1, useNaN: false) { } - public ATR_Series(int period) : this(period: period, useNaN: false) { } - public ATR_Series(TBars source) : this(source, period: 1, useNaN: false) { } - public ATR_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } - else { - _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; - _k = (_period == 0) ? 1 / (double)_len : _k; - _len++; - } - - if (_len == 1) { _cm1 = TBar.c; } - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - _cm1 = TBar.c; - - double _atr = 0; - if (this.Count == 0) { _atr = d.v; } - else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } - else { _atr = _k * (d.v - _lastatr) + _lastatr; } - _lastatr = _atr; - - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atr); - return base.Add(res, update); - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/BBANDS_Series.cs b/Calculations/_Updated/BBANDS_Series.cs deleted file mode 100644 index 7cc5a3e0..00000000 --- a/Calculations/_Updated/BBANDS_Series.cs +++ /dev/null @@ -1,112 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -BBANDS: Bollinger Bands® - Price channels created by John Bollinger, depict volatility as standard deviation boundary - line range from a moving average of price. The bands automatically widen when volatility - increases and contract when volatility decreases. Their dynamic nature allows them to be - used on different securities with the standard settings. - - Mid Band = simple moving average (SMA) - Upper Band = SMA + (standard deviation of price x multiplier) - Lower Band = SMA - (standard deviation of price x multiplier) - Bandwidth = Width of the channel: (Upper-Lower)/SMA - %B = The location of the data point within the channel: (Price-Lower)/(Upper/Lower) - Z-Score = number of standard deviations of the data point from SMA - -Sources: - https://www.investopedia.com/terms/b/bollingerbands.asp - https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands - -Note: - Bollinger Bands® is a registered trademark of John A. Bollinger. - - */ - -public class BBANDS_Series : TSeries { - protected readonly int _period; - protected readonly double _multiplier; - protected readonly bool _NaN; - protected readonly TSeries _data; - public SMA_Series Mid { get; } - public TSeries Upper { get; } - public TSeries Lower { get; } - public TSeries PercentB { get; } - public TSeries Bandwidth { get; } - public TSeries Zscore { get; } - private readonly SDEV_Series _sdev; - - //core constructors - public BBANDS_Series(int period, double multiplier, bool useNaN) { - _period = period; - _multiplier = multiplier; - _NaN = useNaN; - Name = $"BBANDS({period})"; - } - public BBANDS_Series(TSeries source, int period, double multiplier, bool useNaN) : this(period, multiplier, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - Upper = new("BB_Up"); - Lower = new("BB_Low"); - Bandwidth = new("BBandwidth"); - PercentB = new("%BBandwidth"); - Zscore = new("Zscore"); - - Mid = new(period, false); - _sdev = new(period, false); - - _data.Pub += Sub; - Add(_data); - } - - public BBANDS_Series() : this(period:0, multiplier: 2.0, useNaN: false) { } - public BBANDS_Series(int period) : this(period: period, multiplier: 2.0, useNaN:false) { } - public BBANDS_Series(TBars source) : this(source:source.Close, period:0, multiplier: 2.0, useNaN:false) { } - public BBANDS_Series(TBars source, int period) : this(source:source.Close, period:period, multiplier: 2.0, useNaN: false) { } - public BBANDS_Series(TBars source, int period, double multiplier, bool useNaN) : this(source.Close, period:period, multiplier:multiplier, useNaN: false) { } - public BBANDS_Series(TSeries source) : this(source, period:0, useNaN:false) { } - public BBANDS_Series(TSeries source, int period) : this(source:source, period:period, useNaN:false) { } - public BBANDS_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, multiplier: 2.0, useNaN: useNaN) { } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) { - var _mid = Mid.Add(TValue,update); - var _sd = this._sdev.Add(TValue, update); - var _upper = Upper.Add((TValue.t, _mid.v + _sd.v * _multiplier), update); - var _lower = Lower.Add((TValue.t, _mid.v - _sd.v * _multiplier), update); - double _pbdnd = TValue.v - _lower.v; - double _pbdvr = _upper.v - _lower.v; - PercentB.Add((TValue.t, _pbdnd/_pbdvr), update); - Zscore.Add((TValue.t, (TValue.v-_mid.v)/_sd.v), update); - Bandwidth.Add((TValue.t, _pbdvr / _mid.v), update); - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pbdvr / _mid.v); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - //reset calculation - public override void Reset() { - Mid.Clear(); - _sdev.Clear(); - Upper.Clear(); - Lower.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/BIAS_Series.cs b/Calculations/_Updated/BIAS_Series.cs deleted file mode 100644 index 0c859ee4..00000000 --- a/Calculations/_Updated/BIAS_Series.cs +++ /dev/null @@ -1,72 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -BIAS: Rate of change between the source and a moving average. - Bias is a statistical term which means a systematic deviation from the actual value. - -BIAS = (close - SMA) / SMA - = (close / SMA) - 1 - -Sources: - https://en.wikipedia.org/wiki/Bias_of_an_estimator - - */ - -public class BIAS_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly SMA_Series _sma; - - //core constructors - public BIAS_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"BIAS({period})"; - _sma = new(period, false); - } - public BIAS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public BIAS_Series() : this(period: 0, useNaN: false) { } - public BIAS_Series(int period) : this(period: period, useNaN: false) { } - public BIAS_Series(TBars source) : this(source.Close, 0, false) { } - public BIAS_Series(TBars source, int period) : this(source.Close, period, false) { } - public BIAS_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public BIAS_Series(TSeries source) : this(source, 0, false) { } - public BIAS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - var _s = _sma.Add(TValue,update); - double _bias = (TValue.v / ((_s.v!=0)?_s.v:1)) - 1; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _bias); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _sma.Reset(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/CCI_Series.cs b/Calculations/_Updated/CCI_Series.cs deleted file mode 100644 index 89efbc20..00000000 --- a/Calculations/_Updated/CCI_Series.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -CCI: Commodity Channel Index - Commodity Channel Index is a momentum oscillator used to primarily identify overbought - and oversold levels relative to a mean. CCI measures the current price level relative - to an average price level over a given period of time: - - CCI is relatively high when prices are far above their average. - - CCI is relatively low when prices are far below their average. - Using this method, CCI can be used to identify overbought and oversold levels. - -Sources: - https://www.investopedia.com/terms/c/commoditychannelindex.asp - https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci - - */ - -public class CCI_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private readonly System.Collections.Generic.List _tp = new(); - - //core constructors - public CCI_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"CCI({period})"; - } - public CCI_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public CCI_Series() : this(period: 2, useNaN: false) { } - public CCI_Series(int period) : this(period: period, useNaN: false) { } - public CCI_Series(TBars source) : this(source, period: 2, useNaN: false) { } - public CCI_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - double _tpItem = (TBar.h + TBar.l + TBar.c) / 3.0; - if (update) { - this._tp[this._tp.Count - 1] = _tpItem; - } - else { - this._tp.Add(_tpItem); - } - if (this._tp.Count > this._period) { this._tp.RemoveAt(0); } - - // average TP over _tp buffer - double _avgTp = _tp.Average(); - - // average Deviation over _tp buffer - double _avgDv = 0; - for (int i = 0; i < this._tp.Count; i++) { _avgDv += Math.Abs(_avgTp - this._tp[i]); } - _avgDv /= this._tp.Count; - - double _cci = (_avgDv == 0) ? 0 : (this._tp[this._tp.Count - 1] - _avgTp) / (0.015 * _avgDv); - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _cci); - return base.Add(res, update); - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _tp.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/CMO_Series.cs b/Calculations/_Updated/CMO_Series.cs deleted file mode 100644 index ea800ed2..00000000 --- a/Calculations/_Updated/CMO_Series.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -CMO: Chande Momentum Oscillator - Chande Momentum Oscillator (also known as CMO indicator) was developed by Tushar S. Chande - CMO is similar to other momentum oscillators (e.g. RSI or Stochastics). Alike RSI oscillator, - the CMO values move in the range from -100 to +100 points and its aim is to detect the - overbought and oversold market conditions. CMO calculates the price momentum on both the up - days as well as the down days. The CMO calculation is based on non-smoothed price values - meaning that it can reach its extremes more frequently and the short-time swings are more visible. - -Sources: - https://www.technicalindicators.net/indicators-technical-analysis/144-cmo-chande-momentum-oscillator - - */ - -public class CMO_Series : TSeries { - private readonly System.Collections.Generic.List _buff_up = new(); - private readonly System.Collections.Generic.List _buff_dn = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _plast_value, _last_value; - - //core constructors - public CMO_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"CMO({period})"; - } - public CMO_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public CMO_Series() : this(period: 0, useNaN: false) { } - public CMO_Series(int period) : this(period: period, useNaN: false) { } - public CMO_Series(TBars source) : this(source.Close, 0, false) { } - public CMO_Series(TBars source, int period) : this(source.Close, period, false) { } - public CMO_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public CMO_Series(TSeries source) : this(source, 0, false) { } - public CMO_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (update) { _last_value = _plast_value; } else { _plast_value = _last_value; } - BufferTrim(buffer:_buff_up, (TValue.v > _last_value) ? TValue.v - _last_value : 0, period:_period, update: update); - BufferTrim(buffer: _buff_dn, (TValue.v < _last_value) ? _last_value - TValue.v : 0, period: _period, update: update); - _last_value = TValue.v; - double _cmo_up = 0; - double _cmo_dn = 0; - for (int i = 0; i < Math.Min(_buff_up.Count, _buff_dn.Count); i++) { - _cmo_up += _buff_up[i]; - _cmo_dn += _buff_dn[i]; - } - double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn); - if (_cmo_up + _cmo_dn == 0) { _cmo = 0; } - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _cmo); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buff_up.Clear(); - _buff_dn.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/CUSUM_Series.cs b/Calculations/_Updated/CUSUM_Series.cs deleted file mode 100644 index 08897f07..00000000 --- a/Calculations/_Updated/CUSUM_Series.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -CUSUM: Cumulative Sum (aka Running Total) - SUM across a period provides a rolling sum of all values across the period. - If SUM values would be divided with period, the output would be SMA() - -Sources: - https://en.wikipedia.org/wiki/CUSUM - */ - -public class CUSUM_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public CUSUM_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"CUSUM({period})"; - } - public CUSUM_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public CUSUM_Series() : this(period: 0, useNaN: false) { } - public CUSUM_Series(int period) : this(period: period, useNaN: false) { } - public CUSUM_Series(TBars source) : this(source.Close, 0, false) { } - public CUSUM_Series(TBars source, int period) : this(source.Close, period, false) { } - public CUSUM_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public CUSUM_Series(TSeries source) : this(source, period: 0, useNaN: false) { } - public CUSUM_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sum = 0; - for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; } - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sum); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/DECAY_Series.cs b/Calculations/_Updated/DECAY_Series.cs deleted file mode 100644 index 263875c3..00000000 --- a/Calculations/_Updated/DECAY_Series.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -DECAY: - Linear decay can be modeled by a straight line with a negative slope of 1/period. - The value decreases in a straight line from the last maximum to 0. - Decay = Last Max - distance/period - - Exponential decay is modeled as an exponential curve with diminishing factor of - 1-1/p - - */ - -public class DECAY_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly bool _exp; - private double _pdecay, _ppdecay; - private readonly double _dfactor; - - //core constructors - public DECAY_Series(int period, bool exponential, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"DECAY({period})"; - _exp = exponential; - _dfactor = (_exp) ? 1.0 - 1.0 / (double)_period : 1 / (double)_period; - _pdecay = _ppdecay = 0; - } - public DECAY_Series(TSeries source, int period, bool exponential, bool useNaN) : this(period, exponential, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public DECAY_Series() : this(period: 0, exponential: false, useNaN: false) { } - public DECAY_Series(int period) : this(period: period, exponential: false, useNaN: false) { } - public DECAY_Series(TBars source) : this(source.Close, period: 0, exponential: false, useNaN: false) { } - public DECAY_Series(TBars source, int period) : this(source.Close, period: period, exponential:false, useNaN:false) { } - public DECAY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, exponential: false, useNaN) { } - public DECAY_Series(TSeries source) : this(source, period: 0, exponential: false, useNaN:false) { } - public DECAY_Series(TSeries source, int period) : this(source: source, period: period, exponential: false, useNaN: false) { } - public DECAY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, exponential: false, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - if (update) { _pdecay = _ppdecay; } - else { _ppdecay = _pdecay; } - - if (this.Count == 0) { _pdecay = TValue.v; } - double _decay = Math.Max(TValue.v, Math.Max((_exp) ? _pdecay * _dfactor : _pdecay - _dfactor, 0)); - _pdecay = _decay; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _decay); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _pdecay = _ppdecay = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/DEMA_Series.cs b/Calculations/_Updated/DEMA_Series.cs deleted file mode 100644 index dffd9f71..00000000 --- a/Calculations/_Updated/DEMA_Series.cs +++ /dev/null @@ -1,127 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -DEMA: Double Exponential Moving Average - DEMA uses EMA(EMA()) to calculate smoother Exponential moving average. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/ - -Remark: - ema1 = EMA(close, length) - ema2 = EMA(ema1, length) - DEMA = 2 * ema1 - ema2 - - */ - -public class DEMA_Series : TSeries { - private double _k; - private double _sum, _oldsum; - private double _lastema1, _oldema1, _lastema2, _oldema2; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - -//core constructor - public DEMA_Series(int period, bool useNaN, bool useSMA) { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"DEMA({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _sum = _oldsum = _lastema1 = _lastema2 = 0; - } - //generic constructors (source) - - public DEMA_Series() : this(0, false, true) {} - public DEMA_Series(int period) : this(period, false, true) {} - public DEMA_Series(TBars source) : this(source.Close, 0, false) {} - public DEMA_Series(TBars source, int period) : this(source.Close, period, false) {} - public DEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {} - public DEMA_Series(TSeries source, int period) : this(source, period, false, true) {} - public DEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {} - public DEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - -// core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (update) { - _lastema1 = _oldema1; - _lastema2 = _oldema2; - _sum = _oldsum; - } - else { - _oldema1 = _lastema1; - _oldema2 = _lastema2; - _oldsum = _sum; - _len++; - } - - if (_period == 0) { - _k = 2.0 / (_len + 1); - } - - double _ema1, _ema2, _dema; - if (Count == 0) { - _ema1 = _ema2 = _sum = TValue.v; - } - else if (_len <= _period && _useSMA && _period != 0) { - _sum += TValue.v; - _ema1 = _sum / Math.Min(_len, _period); - _ema2 = _ema1; - } - else { - _ema1 = (TValue.v - _lastema1) * _k + _lastema1; - _ema2 = (_ema1 - _lastema2) * _k + _lastema2; - } - - _dema = 2 * _ema1 - _ema2; - - _lastema1 = double.IsNaN(_ema1) ? _lastema1 : _ema1; - _lastema2 = double.IsNaN(_ema2) ? _lastema2 : _ema2; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dema); - return base.Add(res, update); - } - -//variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { - return (DateTime.Today, double.NaN); - } - - foreach (var item in data) { - Add(item, false); - } - - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) { - return Add(_data.Last, update); - } - - public (DateTime t, double v) Add() { - return Add(_data.Last, false); - } - - private new void Sub(object source, TSeriesEventArgs e) { - Add(_data.Last, e.update); - } - - //reset calculation - public override void Reset() { - _sum = _oldsum = _lastema1 = _lastema2 = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/DWMA_Series.cs b/Calculations/_Updated/DWMA_Series.cs deleted file mode 100644 index 20283c34..00000000 --- a/Calculations/_Updated/DWMA_Series.cs +++ /dev/null @@ -1,122 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -/* -DWMA: Double Weighted Moving Average - The weights are decreasing over the period with p^2 decay - and the most recent data has the heaviest weight. - - */ - -public class DWMA_Series : TSeries { - private readonly List _buffer = new(); - private List _weights; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected int _len; - -//core constructors - public DWMA_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"DWMA({period})"; - _len = 0; - _weights = CalculateWeights(_period); - } - - public DWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - public DWMA_Series() : this(0, false) { - } - - public DWMA_Series(int period) : this(period, false) { - } - - public DWMA_Series(TBars source) : this(source.Close, 0, false) { - } - - public DWMA_Series(TBars source, int period) : this(source.Close, period, false) { - } - - public DWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { - } - - public DWMA_Series(TSeries source, int period) : this(source, period, false) { - } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(_buffer, TValue.v, _period, update); - if (_period == 0) { - _len++; - _weights = CalculateWeights(_len); - } - - double _dwma = 0, _wsum = 0; - var bufferCount = _buffer.Count; - - var lockObj = new object(); - Parallel.For(0, bufferCount, i => - { - var temp = _buffer[i] * _weights[i]; - lock (lockObj) { - _dwma += temp; - _wsum += _weights[i]; - } - }); - _dwma /= _wsum; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dwma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { - return (DateTime.Today, double.NaN); - } - - foreach (var item in data) { - Add(item, false); - } - - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) { - return Add(_data.Last, update); - } - - public (DateTime t, double v) Add() { - return Add(_data.Last, false); - } - - private new void Sub(object source, TSeriesEventArgs e) { - Add(_data.Last, e.update); - } - - //calculating weights - private static List CalculateWeights(int period) { - var weights = new List(period); - for (var i = 0; i < period; i++) { - weights.Add((i + 1) * (i + 1)); - } - - return weights; - } - - //reset calculation - public override void Reset() { - _len = 0; - _buffer.Clear(); - _weights = CalculateWeights(_period); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/EMA_Series.cs b/Calculations/_Updated/EMA_Series.cs deleted file mode 100644 index 174d978a..00000000 --- a/Calculations/_Updated/EMA_Series.cs +++ /dev/null @@ -1,120 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -EMA: Exponential Moving Average - EMA needs very short history buffer and calculates the EMA value using just the - previous EMA value. The weight of the new datapoint (k) is k = 2 / (period-1) - -Sources: - https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages - https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp - https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA - -Issues: - There is no consensus what the first EMA value should be - a zero, a first - datapoint, or an average of the initial Period bars. All three starting methods - converge within 20+ bars to the same moving average. Most implementations (including this one) - use SMA() for the first Period bars as a seeding value for EMA. - - */ - -public class EMA_Series : TSeries { - private double _k; - private double _lastema, _oldema; - private double _sum, _oldsum; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - -//core constructors - - public EMA_Series(int period, bool useNaN, bool useSMA) { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"EMA({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _sum = _oldsum = _lastema = _oldema = 0; - } - public EMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public EMA_Series() : this(0, false, true) {} - public EMA_Series(int period) : this(period, false, true) {} - public EMA_Series(TBars source) : this(source.Close, 0, false) {} - public EMA_Series(TBars source, int period) : this(source.Close, period, false) {} - public EMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {} - public EMA_Series(TSeries source, int period) : this(source, period, false, true) {} - public EMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {} - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (update) { - _lastema = _oldema; - _sum = _oldsum; - } - else { - _oldema = _lastema; - _oldsum = _sum; - _len++; - } - - double _ema = 0; - if (_period == 0) { - _k = 2.0 / (_len + 1); - } - - if (Count == 0) { - _ema = _sum = TValue.v; - } - else if (_len <= _period && _useSMA && _period != 0) { - _sum += TValue.v; - if (_period != 0 && _len > _period) { - _sum -= _data[Count - _period - (update ? 1 : 0)].v; - } - - _ema = _sum / Math.Min(_len, _period); - } - else { - _ema = _k * (TValue.v - _lastema) + _lastema; - } - - _lastema = double.IsNaN(_ema) ? _lastema : _ema; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ema); - return base.Add(res, update); - } - -//variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _sum = _oldsum = _lastema = _oldema = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ENTROPY_Series.cs b/Calculations/_Updated/ENTROPY_Series.cs deleted file mode 100644 index 27592dfd..00000000 --- a/Calculations/_Updated/ENTROPY_Series.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -ENTROPY: - Introduced by Claude Shannon in 1948, entropy measures the unpredictability - of the data, or equivalently, of its average information. - -Calculation: - P = close / Σ(close) - ENTROPY = Σ(-P * Log(P) / Log(base)) - -Sources: - https://en.wikipedia.org/wiki/Entropy_(information_theory) - https://math.stackexchange.com/questions/3428693/how-to-calculate-entropy-from-a-set-of-correlated-samples - - */ - -public class ENTROPY_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly double _logbase; - private readonly System.Collections.Generic.List _buffer = new(); - private readonly System.Collections.Generic.List _buff2 = new(); - - //core constructors - public ENTROPY_Series(int period, double logbase, bool useNaN) { - _period = period; - _NaN = useNaN; - _logbase = logbase; - Name = $"ENTROPY({period})"; - } - public ENTROPY_Series(TSeries source, int period, double logbase, bool useNaN) : this(period, logbase, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public ENTROPY_Series() : this(period: 0, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(int period) : this(period: period, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TBars source) : this(source.Close, period: 0, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TBars source, int period) : this(source.Close, period, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, logbase: 2.0, useNaN: useNaN) { } - public ENTROPY_Series(TSeries source) : this(source, period: 0, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TSeries source, int period) : this(source: source, period: period, logbase: 2.0, useNaN: false) { } - public ENTROPY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, logbase: 2.0, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - double _sum = _buffer.Sum(); - double _pp = this._buffer[^1] / _sum; - double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase); - BufferTrim(_buff2, _ppp, _period, update); - double _entp = _buff2.Sum(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _entp); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - _buff2.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/FWMA_Series.cs b/Calculations/_Updated/FWMA_Series.cs deleted file mode 100644 index 81134148..00000000 --- a/Calculations/_Updated/FWMA_Series.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Numerics; -using System.Linq; - -/* -FWMA: Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average - (WMA) where the weights are based on the Fibonacci Sequence. - - */ -public class FWMA_Series : TSeries { - private readonly List _buffer = new(); - private List _weights; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected int _len; - - public FWMA_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"FWMA({period})"; - _len = 0; - _weights = CalculateWeights(_period); - } - - public FWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - public FWMA_Series() : this(period: 0, useNaN: false) { } - public FWMA_Series(int period) : this(period: period, useNaN: false) { } - public FWMA_Series(TBars source) : this(source.Close, 0, false) { } - public FWMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public FWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public FWMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - if (_period == 0) { - _len++; - _weights = CalculateWeights(_len); - } - double _fwma = 0; - double totalWeights = _weights.Sum(); - object lockObj = new object(); - Parallel.For(0, _buffer.Count, i => - { - double temp = _buffer[i] * _weights[i]; - lock (lockObj) { _fwma += temp; } - }); - _fwma /= totalWeights; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fwma); - return base.Add(res, update); - } - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - private static List CalculateWeights(int period) { - //to prevent overflow, max period can be no more than 1476 - period = (period > 1476) ? 1476 : period; - List weights = new List(period); - BigInteger a = 0; - BigInteger b = 1; - for (int i = 0; i < period; i++) { - BigInteger temp = a; - a = b; - b = temp + b; - weights.Add((double)Decimal.Parse(a.ToString())); - } - return weights; - } - - public override void Reset() { - _weights = CalculateWeights(_period); - _buffer.Clear(); - } -} diff --git a/Calculations/_Updated/HEMA_Series.cs b/Calculations/_Updated/HEMA_Series.cs deleted file mode 100644 index 60aacda4..00000000 --- a/Calculations/_Updated/HEMA_Series.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -HEMA: Hull-EMA Moving Average - a hybrid indicator - Modified HUll Moving Average; instead of using WMA (Weighted MA) for calculation, - HEMA uses EMA for Hull's formula: - -EMA1 = EMA(n/2) of price - where k = 4/(n/2 +1) -EMA2 = EMA(n) of price - where k = 3/(n+1) -Raw HMA = (2 * EMA1) - EMA2 -EMA3 = EMA(sqrt(n)) of Raw HMA - where k = 2/(sqrt(n)+1) - */ - -public class HEMA_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _k1, _k2, _k3; - private int _len; - private double _lastema1, _oldema1; - private double _lastema2, _oldema2; - private double _lasthema, _oldhema; - - //core constructors - public HEMA_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"HEMA({period})"; - (_k1, _k2, _k3) = CalculateK(_period); - _len = 0; - _lastema1 = _oldema1 = _lastema2 = _oldema2 = _lasthema = _oldhema = 0; - } - public HEMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public HEMA_Series() : this(period: 0, useNaN: false) { } - public HEMA_Series(int period) : this(period: period, useNaN: false) { } - public HEMA_Series(TBars source) : this(source.Close, 0, false) { } - public HEMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public HEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public HEMA_Series(TSeries source) : this(source, 0, false) { } - public HEMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (update) { - _lastema1 = _oldema1; - _lastema2 = _oldema2; - _lasthema = _oldhema; - } - else { - _oldema1 = _lastema1; - _oldema2 = _lastema2; - _oldhema = _lasthema; - } - double _ema1, _ema2, _hema; - if (_period == 0) { - _len++; - (_k1, _k2, _k3) = CalculateK(_len); - } - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, double.NaN), update); - } else if (this.Count == 0) { - _ema1 = _ema2 = _hema = TValue.v; - } - else { - _ema1 = _k1 * (TValue.v - _lastema1) + _lastema1; - _ema2 = _k2 * (TValue.v - _lastema2) + _lastema2; - _hema = _k3 * (((2 * _ema1) - _ema2) - _lasthema) + _lasthema; - } - - _lastema1 = _ema1; - _lastema2 = _ema2; - _lasthema = _hema; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hema); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _lastema1 = _lastema2 = _lasthema = 0; - _oldema1 = _oldema2 = _oldhema = 0; - _len = 0; - } - - public static (double k1, double k2, double k3) CalculateK(int len) { - double k1 = 8 / (double)(len + 7); - double k2 = 3 / (double)(len + 2); - double k3 = 2 / Math.Sqrt(len + 3); - - return (k1, k2, k3); - } - -} \ No newline at end of file diff --git a/Calculations/_Updated/HMA_Series.cs b/Calculations/_Updated/HMA_Series.cs deleted file mode 100644 index 01736639..00000000 --- a/Calculations/_Updated/HMA_Series.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -HMA: Hull Moving Average - Developed by Alan Hull, an extremely fast and smooth moving average; almost - eliminates lag altogether and manages to improve smoothing at the same time. - -Sources: - https://alanhull.com/hull-moving-average - https://school.stockcharts.com/doku.php?id=technical_indicators:hull_moving_average - -WMA1 = WMA(n/2) of price -WMA2 = WMA(n) of price -Raw HMA = (2 * WMA1) - WMA2 -HMA = WMA(sqrt(n)) of Raw HMA - - */ - -public class HMA_Series : TSeries { - protected int _period, _period2, _psqrt; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected WMA_Series _wma1, _wma2, _wma3; - - //core constructors - public HMA_Series(int period, bool useNaN) { - _period = period; - _period2 = period /2; - _psqrt = (int)Math.Sqrt(period); - _NaN = useNaN; - _wma1 = new(Math.Max(_period2,1), false); - _wma2 = new(Math.Max(_period,1), false); - _wma3 = new(Math.Max(_psqrt,1), useNaN); - Name = $"HMA({period})"; - } - public HMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public HMA_Series() : this(period: 0, useNaN: false) { } - public HMA_Series(int period) : this(period: period, useNaN: false) { } - public HMA_Series(TBars source) : this(source.Close, 0, false) { } - public HMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public HMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public HMA_Series(TSeries source) : this(source, 0, false) { } - public HMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (_period == 0) { - _wma1.Len = this.Count / 2; - _wma2.Len = this.Count; - _wma1.Len = (int)Math.Sqrt(this.Count); - } - double _w1 = _wma1.Add(TValue, update).v; - double _w2 = _wma2.Add(TValue, update).v; - double _hma = _wma3.Add((2 * _w1) - _w2, update).v; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _wma1.Reset(); - _wma2.Reset(); - _wma3.Reset(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/HWMA_Series.cs b/Calculations/_Updated/HWMA_Series.cs deleted file mode 100644 index 8f360b52..00000000 --- a/Calculations/_Updated/HWMA_Series.cs +++ /dev/null @@ -1,132 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -HWMA: Holt-Winter Moving Average - Indicator HWMA (Holt-Winter Moving Average) is a three-parameter moving - average by the Holt-Winter method; Holt-Winters Exponential Smoothing is - used for forecasting time series data that exhibits both a trend and a - seasonal variation. - - -Sources: - https://timeseriesreasoning.com/contents/holt-winters-exponential-smoothing/ - https://www.mql5.com/en/code/20856 - -nA - smoothed series (from 0 to 1) -nB - assess the trend (from 0 to 1) -nC - assess seasonality (from 0 to 1) - -Heuristic for determining alpha, beta, and gamma from period: - alpha = 2 / (1 + period) - beta = 1 / period - gamma = 1 / period - -F[i] = (1-nA) * (F[i-1] + V[i-1] + 0.5 * A[i-1]) + nA * Price[i] -V[i] = (1-nB) * (V[i-1] + A[i-1]) + nB * (F[i] - F[i-1]) -A[i] = (1-nC) * A[i-1] + nC * (V[i] - V[i-1]) -HWMA[i] = F[i] + V[i] + 0.5 * A[i] - - */ - -public class HWMA_Series : TSeries { - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - double _nA, _nB, _nC; - double _pF, _pV, _pA; - double _ppF, _ppV, _ppA; - - //core constructors - - public HWMA_Series(double nA, double nB, double nC, bool useNaN) { - _period = (int)((2 - nA) / nA); - _nA = nA; - _nB = nB; - _nC = nC; - _NaN = useNaN; - Name = $"HWMA({_period})"; - _len = 0; - } - public HWMA_Series(TSeries source, double nA, double nB, double nC, bool useNaN = false) : this(nA, nB, nC, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public HWMA_Series() : this(period: 0, useNaN: false) { } - public HWMA_Series(int period) : this(period, useNaN: false) { } - public HWMA_Series(int period, bool useNaN) : this(nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN) { - _period = period; - } - public HWMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } - public HWMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public HWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public HWMA_Series(TSeries source, int period) : this(source, period, false) { } - public HWMA_Series(TSeries source, int period, bool useNaN) : this(source, nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - double _F, _V, _A; - if (_len == 0) { _pF = TValue.v; _pA = _pV = 0; } - - if (update) { _pF = _ppF; _pV = _ppV; _pA = _ppA; } - else { - _ppF = _pF; - _ppV = _pV; - _ppA = _pA; - _len++; - } - - if (_period == 0) { - _nA = 2 / (1 + (double)_len); - _nB = 1 / (double)_len; - _nC = 1 / (double)_len; - } - if (_period == 1) { - _nA = 1; - _nB = 0; - _nC = 0; - } - - _F = (1 - _nA) * (_pF + _pV + 0.5 * _pA) + _nA * TValue.v; - _V = (1 - _nB) * (_pV + _pA) + _nB * (_F - _pF); - _A = (1 - _nC) * _pA + _nC * (_V - _pV); - - double _hwma = _F + _V + 0.5 * _A; - _pF = _F; - _pV = _V; - _pA = _A; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hwma); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/KAMA_Series.cs b/Calculations/_Updated/KAMA_Series.cs deleted file mode 100644 index 74065605..00000000 --- a/Calculations/_Updated/KAMA_Series.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -KAMA: Kaufman's Adaptive Moving Average - Created in 1988 by American quantitative finance theorist Perry J. Kaufman and is known as - Kaufman's Adaptive Moving Average (KAMA). Even though the method was developed as early as 1972, - it was not until the popular book titled "Trading Systems and Methods" that it was made widely - available to the public. Unlike other conventional moving averages systems, the Kaufman's Adaptive - Moving Average, considers market volatility apart from price fluctuations. - - KAMA[i] = KAMA[i-1] + SC * ( price - KAMA[i-1] ) - -Sources: - https://www.tutorialspoint.com/kaufman-s-adaptive-moving-average-kama-formula-and-how-does-it-work - https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/ - https://www.technicalindicators.net/indicators-technical-analysis/152-kama-kaufman-adaptive-moving-average - -Remark: - If useNaN:true argument is provided, KAMA starts calculating values from [period] bar onwards. - Without useNaN argument (default setting), KAMA starts calculating values from bar 1 - and yields - slightly different results for the first 50 bars - and then converges with the other one. - - */ - -public class KAMA_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - private double _lastkama, _lastlastkama; - private readonly double _scFast, _scSlow; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public KAMA_Series(int period, int fast, int slow, bool useNaN) { - _period = period; - _NaN = useNaN; - _scFast = 2.0 / (((period < fast) ? period : fast) + 1); - _scSlow = 2.0 / (slow + 1); - _lastkama = _lastlastkama = 0; - Name = $"KAMA({period})"; - } - public KAMA_Series(TSeries source, int period, int fast, int slow, bool useNaN) : this(period, fast, slow, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public KAMA_Series() : this(period: 0, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(int period) : this(period: period, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TBars source) : this(source.Close, period: 0, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TBars source, int period) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: useNaN) { } - public KAMA_Series(TSeries source) : this(source, period: 0, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TSeries source, int period) : this(source: source, period: period, fast: 2, slow: 30, useNaN: false) { } - public KAMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, fast: 2, slow: 30, useNaN: useNaN) { } - public KAMA_Series(TSeries source, int period, int fast, int slow) : this(source: source, period: period, fast: fast, slow: slow, useNaN: false) { } - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - - if (update) { _lastkama = _lastlastkama; } - else { _lastlastkama = _lastkama; } - BufferTrim(buffer: _buffer, value: TValue.v, period: _period + 1, update: update); - - double _kama = 0; - if (this.Count < _period) { _kama = TValue.v; } - else { - double _change = Math.Abs(_buffer[^1] - _buffer[(_buffer.Count > _period + 1) ? 1 : 0]); - double _sumpv = 0; - for (int i = 1; i < _buffer.Count; i++) { _sumpv += Math.Abs(_buffer[(_buffer.Count > 0) ? i : 0] - _buffer[i - 1]); } - double _er = (_sumpv == 0) ? 0 : _change / _sumpv; - double _sc = (_er * (_scFast - _scSlow)) + _scSlow; - _kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama))); - } - _lastkama = _kama; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kama); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - _lastkama = _lastlastkama = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/KURTOSIS_Series.cs b/Calculations/_Updated/KURTOSIS_Series.cs deleted file mode 100644 index 2c4e10c8..00000000 --- a/Calculations/_Updated/KURTOSIS_Series.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -KURTOSIS: Kurtosis of population - Kurtosis characterizes the relative peakedness or flatness of a distribution - compared with the normal distribution. Positive kurtosis indicates a relatively - peaked distribution. Negative kurtosis indicates a relatively flat distribution. - - The normal curve is called Mesokurtic curve. If the curve of a distribution is - more outlier prone (or heavier-tailed) than a normal or mesokurtic curve then - it is referred to as a Leptokurtic curve. If a curve is less outlier prone (or - lighter-tailed) than a normal curve, it is called as a platykurtic curve. - -Calculation: - sum4 = Σ(close-SMA)^4 - sum2 = (Σ(close-SMA)^2)^2 - KURTOSIS = length * (sum4/sum2) - -Sources: - https://en.wikipedia.org/wiki/Kurtosis - https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ - - */ - -public class KURTOSIS_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly System.Collections.Generic.List _buffer = new(); - - //core constructors - public KURTOSIS_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"KURTOSIS({period})"; - } - public KURTOSIS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public KURTOSIS_Series() : this(period: 0, useNaN: false) { } - public KURTOSIS_Series(int period) : this(period: period, useNaN: false) { } - public KURTOSIS_Series(TSeries source) : this(source, period: 0, useNaN: false) { } - public KURTOSIS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - double _n = _buffer.Count; - double _avg = _buffer.Average(); - - double _s2 = 0; - double _s4 = 0; - for (int i = 0; i < this._buffer.Count; i++) { - _s2 += (_buffer[i] - _avg) * (_buffer[i] - _avg); - _s4 += (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg); - } - - double _Vx = _s2 / (_n - 1); - double _kurt = (_n > 3) ? - (_n * (_n + 1) * _s4) / (_Vx * _Vx * (_n - 3) * (_n - 1) * (_n - 2)) - (3 * (_n - 1) * (_n - 1) / ((_n - 2) * (_n - 3))) //using Sheskin Algo - : (_s2 * _s2) / _n - 3; //using Snedecor and Cochran (1967) algo - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kurt); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MACD_Series.cs b/Calculations/_Updated/MACD_Series.cs deleted file mode 100644 index 20d8c86b..00000000 --- a/Calculations/_Updated/MACD_Series.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MACD: Moving Average Convergence/Divergence - Moving average convergence divergence (MACD) is a trend-following momentum - indicator that shows the relationship between two moving averages of a series. - The MACD is calculated by subtracting the 26-period exponential moving average (EMA) - from the 12-period EMA. MACD Signal is 9-day EMA of MACD. - - */ - -public class MACD_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - - protected readonly int _slow, _fast, _signal; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly EMA_Series _TSlow; - private readonly EMA_Series _TFast; - public EMA_Series Signal { get; } - - //core constructors - public MACD_Series(int slow = 26, int fast = 12, int signal = 9, bool useNaN = false) { - _slow = slow; - _fast = fast; - _signal = signal; - _NaN = useNaN; - Name = $"MACD({slow},{fast},{signal})"; - _TSlow = new(slow, useNaN:false, useSMA:true); - _TFast = new(fast, useNaN: false, useSMA: true); - Signal = new(signal, useNaN: false, useSMA: true); - } - public MACD_Series(TSeries source, int slow, int fast, int signal, bool useNaN) : this(slow, fast, signal, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MACD_Series(TSeries source) : this(source:source, slow:26, fast:12, signal:9 , useNaN:false) { } - public MACD_Series(TSeries source, int slow, int fast, int signal) : this(source: source, slow: slow, fast:fast, signal:signal, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - - var _sslow = _TSlow.Add(TValue,update); - var _sfast = _TFast.Add(TValue, update); - Signal.Add((TValue.t, _sfast.v-_sslow.v)); - - var res = (TValue.t, Count < _fast - 1 && _NaN ? double.NaN : _sfast.v-_sslow.v); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MAD_Series.cs b/Calculations/_Updated/MAD_Series.cs deleted file mode 100644 index 4f18a04b..00000000 --- a/Calculations/_Updated/MAD_Series.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAD: Mean Absolute Deviation - Also known as AAD - Average Absolute Deviation, to differentiate it from Median Absolute Deviation - MAD defines the degree of variation across the series. - -Calculation: - MAD = Σ(|close-SMA|) / period - -Sources: - https://en.wikipedia.org/wiki/Average_absolute_deviation - - */ - -public class MAD_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAD_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MAD({period})"; - } - public MAD_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAD_Series() : this(period: 0, useNaN: false) { } - public MAD_Series(int period) : this(period: period, useNaN: false) { } - public MAD_Series(TBars source) : this(source.Close, 0, false) { } - public MAD_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAD_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAD_Series(TSeries source) : this(source, 0, false) { } - public MAD_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - double _mad = 0; - for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); } - _mad /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mad); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MAE_Series.cs b/Calculations/_Updated/MAE_Series.cs deleted file mode 100644 index 9bf7a6a9..00000000 --- a/Calculations/_Updated/MAE_Series.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAE: Mean Absolute Error - Defined as a Mean (Average) of the absolute difference between actual and estimated values. - MAE = (1/n) * Σ|y_i - MA_i| - -Sources: - https://en.wikipedia.org/wiki/Mean_absolute_error - - */ - -public class MAE_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MSE({period})"; - } - public MAE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAE_Series() : this(period: 0, useNaN: false) { } - public MAE_Series(int period) : this(period: period, useNaN: false) { } - public MAE_Series(TBars source) : this(source.Close, 0, false) { } - public MAE_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAE_Series(TSeries source) : this(source, 0, false) { } - public MAE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _mae = 0; - for (int i = 0; i < _buffer.Count; i++) { _mae += Math.Abs(_buffer[i] - _sma); } - _mae /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mae); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MAMA_Series.cs b/Calculations/_Updated/MAMA_Series.cs deleted file mode 100644 index c4dd2a51..00000000 --- a/Calculations/_Updated/MAMA_Series.cs +++ /dev/null @@ -1,189 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -MAMA: MESA Adaptive Moving Average - Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of - high/low price that uses classic electrical radio-frequency signal processing algorithms - to reduce noise. - - KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 ) - -Sources: - https://mesasoftware.com/papers/MAMA.pdf - https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/ - - */ - -public class MAMA_Series : TSeries { - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - private double sumPr; - private double fastl, slowl; - private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt; - private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama; - public TSeries Fama { get; } - private double mamaseed, famaseed; - - //core constructors - - public MAMA_Series(double fastlimit, double slowlimit, bool useNaN) { - _period = (int)(2 / fastlimit) - 1; - fastl = fastlimit; - slowl = slowlimit; - Fama = new TSeries(); - _NaN = useNaN; - Name = $"MAMA({_period})"; - _len = 0; - } - public MAMA_Series(TSeries source, double fastlimit, double slowlimit, bool useNaN = false) : this(fastlimit, slowlimit, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAMA_Series() : this(period: 0, useNaN: false) { } - public MAMA_Series(int period) : this(period, useNaN: false) { } - public MAMA_Series(int period, bool useNaN) : this(fastlimit: 2 / (period + 1), slowlimit: 0.2 / (period + 1), useNaN) { - _period = period; - } - public MAMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } - public MAMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAMA_Series(TSeries source, int period) : this(source, period, false) { } - public MAMA_Series(TSeries source, int period, bool useNaN) : this(source, fastlimit: 2 / ((double)period + 1), slowlimit: 0.2 / ((double)period + 1), useNaN: useNaN) { } - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - if (!update) { - // roll forward (oldx = x) - pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i; - i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i; - q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i; - dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i; - sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i; - i2.io = i2.i1; i2.i1 = i2.i; q2.io = q2.i1; q2.i1 = q2.i; - re.io = re.i1; re.i1 = re.i; im.io = im.i1; im.i1 = im.i; - pd.io = pd.i1; pd.i1 = pd.i; ph.io = ph.i1; ph.i1 = ph.i; - mama.io = mama.i1; mama.i1 = mama.i; - fama.io = fama.i1; - fama.i1 = fama.i; - _len++; - } - if (_period == 0) { - fastl = 2 / (double)_len; - slowl = fastl * 0.1; - } - if (_period == 1) { - fastl = 1; - slowl = 1; - } - var i = _len - 1; - pr.i = TValue.v; - if (i > 5) { - var adj = 0.075 * pd.i1 + 0.54; - - // smooth and detrender - sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10; - dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj; - - // in-phase and quadrature - q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj; - i1.i = dt.i3; - - // advance the phases by 90 degrees - double jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj; - double jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj; - - // phasor addition for 3-bar averaging - i2.i = i1.i - jQ; - q2.i = q1.i + jI; - - i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it - q2.i = 0.2 * q2.i + 0.8 * q2.i1; - - // homodyne discriminator - re.i = i2.i * i2.i1 + q2.i * q2.i1; - im.i = i2.i * q2.i1 - q2.i * i2.i1; - - re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it - im.i = 0.2 * im.i + 0.8 * im.i1; - - // calculate period - pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d; - - // adjust period to thresholds - pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i; - pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i; - pd.i = pd.i < 6d ? 6d : pd.i; - pd.i = pd.i > 50d ? 50d : pd.i; - - // smooth the period - pd.i = 0.2 * pd.i + 0.8 * pd.i1; - - // determine phase position - ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0; - - // change in phase - var delta = Math.Max(ph.i1 - ph.i, 1d); - - // adaptive alpha value - var alpha = Math.Max(fastl / delta, slowl); - - // final indicators - mama.i = alpha * (pr.i - mama.i1) + mama.i1; - fama.i = 0.5d * alpha * (mama.i - fama.i1) + fama.i1; - } - else { - sumPr += pr.i; - pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0; - mama.i = fama.i = sumPr / (i + 1); - - if (_len == 1) { - mamaseed = famaseed = TValue.v; - } - else { - mamaseed = fastl * (TValue.v - mamaseed) + mamaseed; - famaseed = slowl * (TValue.v - famaseed) + famaseed; - } - } - - double _fama = (i > 5) ? fama.i : famaseed; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fama); - Fama.Add(res, update); - double _mama = (i > 5) ? mama.i : mamaseed; - res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mama); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MAPE_Series.cs b/Calculations/_Updated/MAPE_Series.cs deleted file mode 100644 index 944daf86..00000000 --- a/Calculations/_Updated/MAPE_Series.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAPE: Mean Absolute Percentage Error - Measures the size of the error in percentage terms - -Calculation: - MAPE = Σ(|close – SMA| / |close|) / n - -Sources: - https://en.wikipedia.org/wiki/Mean_absolute_percentage_error - -Remark: - returns infinity if any of observations is 0. - Use SMAPE or WMAPE instead to avoid division-by-zero in MAPE - - */ - -public class MAPE_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAPE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MAPE({period})"; - } - public MAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAPE_Series() : this(period: 0, useNaN: false) { } - public MAPE_Series(int period) : this(period: period, useNaN: false) { } - public MAPE_Series(TBars source) : this(source.Close, 0, false) { } - public MAPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAPE_Series(TSeries source) : this(source, 0, false) { } - public MAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _mape = 0; - for (int i = 0; i < _buffer.Count; i++) { - _mape += (_buffer[i] != 0) ? Math.Abs(_buffer[i] - _sma) / Math.Abs(_buffer[i]) : double.PositiveInfinity; - } - _mape /= (_buffer.Count > 0) ? _buffer.Count : 1; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mape); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MAX_Series.cs b/Calculations/_Updated/MAX_Series.cs deleted file mode 100644 index 8a2facff..00000000 --- a/Calculations/_Updated/MAX_Series.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MAX - Maximum value in the given period in the series. - If period = 0 => period = full length of the series - - */ - -public class MAX_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MAX_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MAX({period})"; - } - public MAX_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MAX_Series() : this(period: 0, useNaN: false) { } - public MAX_Series(int period) : this(period: period, useNaN: false) { } - public MAX_Series(TBars source) : this(source.Close, 0, false) { } - public MAX_Series(TBars source, int period) : this(source.Close, period, false) { } - public MAX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MAX_Series(TSeries source) : this(source, 0, false) { } - public MAX_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _max= _buffer.Max(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MEDIAN_Series.cs b/Calculations/_Updated/MEDIAN_Series.cs deleted file mode 100644 index fde7c634..00000000 --- a/Calculations/_Updated/MEDIAN_Series.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MED - Median value - Median of numbers is the middlemost value of the given set of numbers. - It separates the higher half and the lower half of a given data sample. - At least half of the observations are smaller than or equal to median - and at least half of the observations are greater than or equal to the median. - - If the number of values is odd, the middlemost observation of the sorted - list is the median of the given data. If the number of values is even, - median is the average of (n/2)th and [(n/2) + 1]th values of the sorted list. - - If period = 0 => period is max - -Sources: - https://corporatefinanceinstitute.com/resources/knowledge/other/median/ - https://en.wikipedia.org/wiki/Median - - */ - -public class MEDIAN_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MEDIAN_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MEDIAN({period})"; - } - public MEDIAN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MEDIAN_Series() : this(period: 0, useNaN: false) { } - public MEDIAN_Series(int period) : this(period: period, useNaN: false) { } - public MEDIAN_Series(TBars source) : this(source.Close, 0, false) { } - public MEDIAN_Series(TBars source, int period) : this(source.Close, period, false) { } - public MEDIAN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MEDIAN_Series(TSeries source) : this(source, 0, false) { } - public MEDIAN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - System.Collections.Generic.List _s = new(this._buffer); - _s.Sort(); - int _p1 = _s.Count / 2; - int _p2 = Math.Max(0, (_s.Count / 2) - 1); - double _med = (_s.Count % 2 != 0) ? _s[_p1] : (_s[_p1] + _s[_p2]) / 2; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _med); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MIDPOINT_Series.cs b/Calculations/_Updated/MIDPOINT_Series.cs deleted file mode 100644 index c410be23..00000000 --- a/Calculations/_Updated/MIDPOINT_Series.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MIDPOINT: Midpoint value (max+min)/2 in the given period in the series. - If period = 0 => period = full length of the series - -Sources: - https://thefaqblog.com/what-is-the-midpoint-in-statistics/ - - */ - -public class MIDPOINT_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MIDPOINT_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MIDPOINT({period})"; - } - public MIDPOINT_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MIDPOINT_Series() : this(period: 0, useNaN: false) { } - public MIDPOINT_Series(int period) : this(period: period, useNaN: false) { } - public MIDPOINT_Series(TBars source) : this(source.Close, 0, false) { } - public MIDPOINT_Series(TBars source, int period) : this(source.Close, period, false) { } - public MIDPOINT_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MIDPOINT_Series(TSeries source) : this(source, 0, false) { } - public MIDPOINT_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _max= _buffer.Max(); - double _min = _buffer.Min(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : (_max+_min)*0.5); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MIDPRICE_Series.cs b/Calculations/_Updated/MIDPRICE_Series.cs deleted file mode 100644 index 5b2bf73f..00000000 --- a/Calculations/_Updated/MIDPRICE_Series.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -MIDPRICE: Midpoint price (highhest high + lowest low)/2 in the given period in the series. - If period = 0 => period = full length of the series - - */ - -public class MIDPRICE_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private readonly System.Collections.Generic.List _bufferhi = new(); - private readonly System.Collections.Generic.List _bufferlo = new(); - - //core constructors - public MIDPRICE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MIDPRICE({period})"; - } - public MIDPRICE_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public MIDPRICE_Series() : this(period: 2, useNaN: false) { } - public MIDPRICE_Series(int period) : this(period: period, useNaN: false) { } - public MIDPRICE_Series(TBars source) : this(source, period: 2, useNaN: false) { } - public MIDPRICE_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - BufferTrim(_bufferhi, TBar.h, _period, update); - BufferTrim(_bufferlo, TBar.l, _period, update); - double _mid = (_bufferhi.Max() + _bufferlo.Min()) * 0.5; - - var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _mid); - return base.Add(res, update); - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _bufferhi.Clear(); - _bufferlo.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MIN_Series.cs b/Calculations/_Updated/MIN_Series.cs deleted file mode 100644 index 2b34aa7b..00000000 --- a/Calculations/_Updated/MIN_Series.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MIN - Minimum value in the given period in the series. - If period = 0 => period = full length of the series - - */ - -public class MIN_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MIN_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MAX({period})"; - } - public MIN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MIN_Series() : this(period: 0, useNaN: false) { } - public MIN_Series(int period) : this(period: period, useNaN: false) { } - public MIN_Series(TBars source) : this(source.Close, 0, false) { } - public MIN_Series(TBars source, int period) : this(source.Close, period, false) { } - public MIN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MIN_Series(TSeries source) : this(source, 0, false) { } - public MIN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _max= _buffer.Min(); - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/MSE_Series.cs b/Calculations/_Updated/MSE_Series.cs deleted file mode 100644 index a918962e..00000000 --- a/Calculations/_Updated/MSE_Series.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -MSE: Mean Square Error - Defined as a Mean (Average) of the Square of the difference between actual and estimated values. - -Sources: - https://en.wikipedia.org/wiki/Mean_squared_error - - */ - -public class MSE_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public MSE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"MSE({period})"; - } - public MSE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public MSE_Series() : this(period: 0, useNaN: false) { } - public MSE_Series(int period) : this(period: period, useNaN: false) { } - public MSE_Series(TBars source) : this(source.Close, 0, false) { } - public MSE_Series(TBars source, int period) : this(source.Close, period, false) { } - public MSE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public MSE_Series(TSeries source) : this(source, 0, false) { } - public MSE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _mse = 0; - for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _mse /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mse); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/OBV_Series.cs b/Calculations/_Updated/OBV_Series.cs deleted file mode 100644 index 40aa2c6e..00000000 --- a/Calculations/_Updated/OBV_Series.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -OBV: On-Balance Volume - On-balance volume (OBV) is a technical trading momentum indicator that uses volume flow to predict - changes in stock price. Joseph Granville first developed the OBV metric in the 1963 book - Granville's New Key to Stock Market Profits. - - | +volume; if close > close[previous] - OBV = OBV[previous] + | 0; if close = close[previous] - | -volume; if close < close[previous] - -Sources: - https://www.investopedia.com/terms/o/onbalancevolume.asp - https://www.tradingview.com/wiki/On_Balance_Volume_(OBV) - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/on-balance-volume-obv/ - https://www.motivewave.com/studies/on_balance_volume.htm - -Note: - There is no consensus on what is the first OBV value in the series: - - TA-LIB uses the first volume: OBV[0] = volume[0] - - Skender stock library uses 0: OBV[0] = 0 - - */ - -public class OBV_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TBars _data; - private double _lastobv, _lastlastobv; - private double _lastclose, _lastlastclose; - - //core constructors - public OBV_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"OBV({period})"; - this._lastobv = this._lastlastobv = 0; - this._lastclose = this._lastlastclose = 0; - } - public OBV_Series(TBars source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(data: _data); - } - public OBV_Series() : this(period: 2, useNaN: false) { } - public OBV_Series(int period) : this(period: period, useNaN: false) { } - public OBV_Series(TBars source) : this(source, period: 2, useNaN: false) { } - public OBV_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - - if (update) { - this._lastobv = this._lastlastobv; - this._lastclose = this._lastlastclose; - } - - double _obv = this._lastobv; - if (TBar.c > this._lastclose) { _obv += TBar.v; } - if (TBar.c < this._lastclose) { _obv -= TBar.v; } - - this._lastlastobv = this._lastobv; - this._lastobv = _obv; - - this._lastlastclose = this._lastclose; - this._lastclose = TBar.c; - - var res = (TBar.t, (this.Count < this._period && this._NaN) ? double.NaN : _obv); - return base.Add(res, update); - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - this._lastobv = this._lastlastobv = 0; - this._lastclose = this._lastlastclose = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/RMA_Series.cs b/Calculations/_Updated/RMA_Series.cs deleted file mode 100644 index 5a624bd9..00000000 --- a/Calculations/_Updated/RMA_Series.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -RMA: wildeR Moving Average - J. Welles Wilder introduced RMA as an alternative to EMA. RMA's weight (k) is - set as 1/period, giving less weight to the new data compared to EMA. - -Sources: - https://archive.org/details/newconceptsintec00wild/page/23/mode/2up - https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing - https://www.incrediblecharts.com/indicators/wilder_moving_average.php - -Issues: - Pandas-TA library calculates RMA using straight Exponential Weighted Mean: - pandas.ewm().mean() and returns incorrect first (period) of bars compared to - published formula. This implementation passess the validation test in Wilder's book. - - */ - -public class RMA_Series : TSeries { - private double _k; - private double _lastrma, _oldrma; - private double _sum, _oldsum; - private readonly bool _useSMA; - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - -//core constructor - public RMA_Series(int period, bool useNaN, bool useSMA) { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"RMA({period})"; - _k = 1.0 / (double)(this._period); - _len = 0; - _sum = _oldsum = _lastrma = _oldrma = 0; - } - //generic constructors (source) - - public RMA_Series() : this(0, false, true) {} - public RMA_Series(int period) : this(period, false, true) {} - public RMA_Series(TBars source) : this(source.Close, 0, false) {} - public RMA_Series(TBars source, int period) : this(source.Close, period, false) {} - public RMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {} - public RMA_Series(TSeries source, int period) : this(source, period, false, true) {} - public RMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {} - public RMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - -// core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (update) { - _lastrma = _oldrma; - _sum = _oldsum; - } - else { - _oldrma = _lastrma; - _oldsum = _sum; - _len++; - } - - double _rma = 0; - if (_period == 0) { - _k = 1.0 / (double)(this._len); - } - - if (Count == 0) { - _rma = _sum = TValue.v; - - } else if (_len <= _period && _useSMA && _period != 0) { - _sum += TValue.v; - if (_period != 0 && _len > _period) { - _sum -= _data[Count - _period - (update ? 1 : 0)].v; - } - _rma = _sum / Math.Min(_len, _period); - } - else { - _rma = _k * (TValue.v - _lastrma) + _lastrma; - } - - _lastrma = double.IsNaN(_rma) ? _lastrma : _rma; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rma); - return base.Add(res, update); - } - -//variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _sum = _oldsum = _lastrma = _oldrma = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/RSI_Series.cs b/Calculations/_Updated/RSI_Series.cs deleted file mode 100644 index 8d3979a1..00000000 --- a/Calculations/_Updated/RSI_Series.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -RSI: Relative Strength Index - Created by J. Welles Wilder, the Relative Strength Index measures strength - of the winning/losing streak over N lookback periods on a scale of 0 to 100, - to depict overbought and oversold conditions. - -Sources: - https://www.investopedia.com/terms/r/rsi.asp - - */ - -public class RSI_Series : TSeries { - private readonly System.Collections.Generic.List _gain = new(); - private readonly System.Collections.Generic.List _loss = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _avgGain, _avgLoss, _lastValue; - private double _avgGain_o, _avgLoss_o, _lastValue_o; - private int i; - - //core constructors - public RSI_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"RSI({period})"; - i = 0; - } - public RSI_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public RSI_Series() : this(period: 0, useNaN: false) { } - public RSI_Series(int period) : this(period: period, useNaN: false) { } - public RSI_Series(TBars source) : this(source.Close, 0, false) { } - public RSI_Series(TBars source, int period) : this(source.Close, period, false) { } - public RSI_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public RSI_Series(TSeries source) : this(source, 0, false) { } - public RSI_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - - double _rsi = 0; - if (update) { - _lastValue = _lastValue_o; - _avgGain = _avgGain_o; - _avgLoss = _avgLoss_o; - } - else { - _lastValue_o = _lastValue; - _avgGain_o = _avgGain; - _avgLoss_o = _avgLoss; - } - - if (i == 0) { _lastValue = TValue.v; } - - double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0; - BufferTrim(_gain, _gainval, _period, update); - double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0; - BufferTrim(_loss, _lossval, _period, update); - _lastValue = TValue.v; - - // calculate RSI - if (i > _period && _period != 0) { - _avgGain = ((_avgGain * (_period - 1)) + _gain[^1]) / _period; - _avgLoss = ((_avgLoss * (_period - 1)) + _loss[^1]) / _period; - if (_avgLoss > 0) { - double rs = _avgGain / _avgLoss; - _rsi = 100 - (100 / (1 + rs)); - } - else { _rsi = 100; } - } - // initialize average gain - else { - double _sumGain = 0; - for (int p = 0; p < _gain.Count; p++) { _sumGain += _gain[p]; } - double _sumLoss = 0; - for (int p = 0; p < _loss.Count; p++) { _sumLoss += _loss[p]; } - - _avgGain = _sumGain / _gain.Count; - _avgLoss = _sumLoss / _loss.Count; - - _rsi = (_avgLoss > 0) ? 100 - (100 / (1 + (_avgGain / _avgLoss))) : 100; - } - if (!update) { i++; } - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rsi); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - i = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/SDEV_Series.cs b/Calculations/_Updated/SDEV_Series.cs deleted file mode 100644 index ab5ca503..00000000 --- a/Calculations/_Updated/SDEV_Series.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SDEV: Population Standard Deviation - Population Standard Deviation is the square root of the biased variance, also knons as - Uncorrected Sample Standard Deviation - -Sources: - https://en.wikipedia.org/wiki/Standard_deviation#Uncorrected_sample_standard_deviation - -Remark: - SDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation. - For unbiased version that uses Bessel's correction, use SDEV instead. - - */ - -public class SDEV_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SDEV_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"SDEV({period})"; - } - public SDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SDEV_Series() : this(period: 0, useNaN: false) { } - public SDEV_Series(int period) : this(period: period, useNaN: false) { } - public SDEV_Series(TBars source) : this(source.Close, 0, false) { } - public SDEV_Series(TBars source, int period) : this(source.Close, period, false) { } - public SDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SDEV_Series(TSeries source) : this(source, 0, false) { } - public SDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _var = 0; - for (int i = 0; i < _buffer.Count; i++) { _var += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _var /= this._buffer.Count; - double _sdev = Math.Sqrt(_var); - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sdev); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/SLOPE_Series.cs b/Calculations/_Updated/SLOPE_Series.cs deleted file mode 100644 index 4aef642e..00000000 --- a/Calculations/_Updated/SLOPE_Series.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SLOPE: Slope of linear regression (using Least Square Method) - Linear Regression provides a slope of a straight line that is the best approximation of the given set of data. - The method of least squares is a standard approach in linear regression analysis to approximate the solution - by minimizing the sum of the squares of the residuals made in the results of each individual equation. - -Additional outputs provided by LINREG: - .Intercept - y-intercept point of the best fit line - .RSquared - R-Squared (R²), Coefficient of Determination - .StdDev - Standard Deviation of data over given periods - - y = Slope * x + Intercept - -Sources: - https://en.wikipedia.org/wiki/Least_squares - - */ - -public class SLOPE_Series : TSeries { - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly TSeries p_Intercept = new(); - private readonly TSeries p_RSquared = new(); - private readonly TSeries p_StdDev = new(); - private readonly System.Collections.Generic.List _buffer = new(); - public TSeries Intercept => p_Intercept; - public TSeries RSquared => p_RSquared; - public TSeries StdDev => p_StdDev; - //core constructors - public SLOPE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"SLOPE({period})"; - } - public SLOPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SLOPE_Series() : this(period: 0, useNaN: false) { } - public SLOPE_Series(int period) : this(period: period, useNaN: false) { } - public SLOPE_Series(TBars source) : this(source.Close, 0, false) { } - public SLOPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public SLOPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SLOPE_Series(TSeries source) : this(source, 0, false) { } - public SLOPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - int _len = this._buffer.Count; - - // get averages for period - double sumX = 0; - double sumY = 0; - - for (int p = 0; p < _len; p++) { - sumX += this.Count - _len + 2 + p; - sumY += _buffer[p]; - } - double avgX = sumX / _len; - double avgY = sumY / _len; - - // least squares method - double sumSqX = 0; - double sumSqY = 0; - double sumSqXY = 0; - - for (int p = 0; p < _len; p++) { - double devX = this.Count - _len + 2 + p - avgX; - double devY = _buffer[p] - avgY; - - sumSqX += devX * devX; - sumSqY += devY * devY; - sumSqXY += devX * devY; - } - - double _slope = sumSqXY / sumSqX; - double _intercept = avgY - (_slope * avgX); - - // calculate Standard Deviation and R-Squared - double stdDevX = Math.Sqrt(sumSqX / _len); - double stdDevY = Math.Sqrt(sumSqY / _len); - double _StdDev = stdDevY; - - double arrr = (stdDevX * stdDevY != 0) ? sumSqXY / (stdDevX * stdDevY) / _len : 0; - double _RSquared = arrr * arrr; - - var ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _intercept); - p_Intercept.Add(ret, update); - - ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _StdDev); - p_StdDev.Add(ret, update); - - ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _RSquared); - p_RSquared.Add(ret, update); - - ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _slope); - return base.Add(ret, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/SMAPE_Series.cs b/Calculations/_Updated/SMAPE_Series.cs deleted file mode 100644 index b79a3442..00000000 --- a/Calculations/_Updated/SMAPE_Series.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SMAPE: Symmetric Mean Absolute Percentage Error - Measures the size of the error in percentage terms - -Sources: - https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error - - */ - -public class SMAPE_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SMAPE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"SMAPE({period})"; - } - public SMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SMAPE_Series() : this(period: 0, useNaN: false) { } - public SMAPE_Series(int period) : this(period: period, useNaN: false) { } - public SMAPE_Series(TBars source) : this(source.Close, 0, false) { } - public SMAPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public SMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SMAPE_Series(TSeries source) : this(source, 0, false) { } - public SMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - double _smape = 0; - for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); } - _smape /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smape); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/SMA_Series.cs b/Calculations/_Updated/SMA_Series.cs deleted file mode 100644 index c9a817f0..00000000 --- a/Calculations/_Updated/SMA_Series.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SMA: Simple Moving Average - The weights are equally distributed across the period, resulting in a mean() of - the data within the period - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/ - https://stats.stackexchange.com/a/24739 - -Remark: - This calc doesn't use LINQ or SUM() or any of (slow) iterative methods. It is not as fast as TA-LIB - implementation, but it does allow incremental additions of inputs and real-time calculations of SMA() - - */ -public class SMA_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - - private double _sum, _oldsum; - private readonly int _period; - private readonly TSeries _data; - protected readonly bool _NaN; - - //core constructor - public SMA_Series(int period, bool useNaN) { - _period = Math.Max(0, period); - _NaN = useNaN; - Name = $"SMA({period})"; - _sum = _oldsum = 0; - } - public SMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SMA_Series() : this(0, false) {} - public SMA_Series(int period) : this(period, false) {} - public SMA_Series(TBars source) : this(source.Close, 0, false) {} - public SMA_Series(TBars source, int period) : this(source.Close, period, false) {} - public SMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {} - public SMA_Series(TSeries source) : this(source, 0, false) {} - public SMA_Series(TSeries source, int period) : this(source, period, false) {} - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { return (TValue.t, double.NaN); - } else { - if (update && _buffer.Count > 0) { - _sum -= _buffer[^1]; - _buffer[^1] = TValue.v; - _oldsum = _sum; - } - else { - _buffer.Add(TValue.v); - _oldsum = _sum; - } - - _sum += TValue.v; - if (_period != 0 && _buffer.Count > _period) { - _sum -= _buffer[0]; - _buffer.RemoveAt(0); - } - } - - double _div = _period == 0 ? _buffer.Count : Math.Min(_buffer.Count, _period); - var _sma = _sum / _div; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sma); - return base.Add(res, update); - } - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - - //reset calculation - public override void Reset() { - _sum = _oldsum = 0; - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/SMMA_Series.cs b/Calculations/_Updated/SMMA_Series.cs deleted file mode 100644 index 8f464cc1..00000000 --- a/Calculations/_Updated/SMMA_Series.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -SMMA: Smoothed Moving Average - The Smoothed Moving Average (SMMA) is a combination of a SMA and an EMA. It gives the recent prices - an equal weighting as the historic prices as it takes all available price data into account. - The main advantage of a smoothed moving average is that it removes short-term fluctuations. - - SMMA(i) = (SMMA-1*(N-1) + CLOSE (i)) / N - -Sources: - https://blog.earn2trade.com/smoothed-moving-average - https://guide.traderevolution.com/traderevolution/mobile-applications/phone/android/technical-indicators/moving-averages/smma-smoothed-moving-average - https://www.chartmill.com/documentation/technical-analysis-indicators/217-MOVING-AVERAGES-%7C-The-Smoothed-Moving-Average-%28SMMA%29 - - */ - -public class SMMA_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private double _lastsmma, _lastlastsmma; - - //core constructors - public SMMA_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"SMMA({period})"; - } - public SMMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SMMA_Series() : this(period: 0, useNaN: false) { } - public SMMA_Series(int period) : this(period: period, useNaN: false) { } - public SMMA_Series(TBars source) : this(source.Close, 0, false) { } - public SMMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public SMMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SMMA_Series(TSeries source) : this(source, 0, false) { } - public SMMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, double.NaN),update); - } - - double _smma = 0; - if (update) { this._lastsmma = this._lastlastsmma; } - - if (this.Count < this._period) { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - _smma = _buffer.Average(); - } - else { - _smma = ((_lastsmma * (_period - 1)) + TValue.v) / _period; - } - - this._lastlastsmma = this._lastsmma; - this._lastsmma = _smma; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - this._lastsmma = this._lastlastsmma = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/SSDEV_Series.cs b/Calculations/_Updated/SSDEV_Series.cs deleted file mode 100644 index 2b5f62d3..00000000 --- a/Calculations/_Updated/SSDEV_Series.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -SSDEV: (Corrected) Sample Standard Deviation - Sample Standard Deviaton uses Bessel's correction to correct the bias in the variance. - -Sources: - https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation - Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction - -Remark: - SSDEV (Sample Standard Deviation) is also known as a unbiased/corrected Standard Deviation. - For a population/biased/uncorrected Standard Deviation, use PSDEV instead - - */ - -public class SSDEV_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SSDEV_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"SSDEV({period})"; - } - public SSDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SSDEV_Series() : this(period: 0, useNaN: false) { } - public SSDEV_Series(int period) : this(period: period, useNaN: false) { } - public SSDEV_Series(TBars source) : this(source.Close, 0, false) { } - public SSDEV_Series(TBars source, int period) : this(source.Close, period, false) { } - public SSDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SSDEV_Series(TSeries source) : this(source, 0, false) { } - public SSDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _svar = 0; - for (int i = 0; i < this._buffer.Count; i++) { _svar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _svar /= (_buffer.Count > 1) ? _buffer.Count - 1 : 1; // Bessel's correction - double _ssdev = Math.Sqrt(_svar); - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ssdev); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/SVAR_Series.cs b/Calculations/_Updated/SVAR_Series.cs deleted file mode 100644 index bb693c54..00000000 --- a/Calculations/_Updated/SVAR_Series.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -VAR: Population Variance - Population variance without Bessel's correction - -Sources: - https://en.wikipedia.org/wiki/Variance - Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction - -Remark: - VAR (Population Variance) is also known as a biased Sample Variance. For unbiased - sample variance use SVAR instead. - - */ - -public class SVAR_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public SVAR_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"SVAR({period})"; - } - public SVAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public SVAR_Series() : this(period: 0, useNaN: false) { } - public SVAR_Series(int period) : this(period: period, useNaN: false) { } - public SVAR_Series(TBars source) : this(source.Close, 0, false) { } - public SVAR_Series(TBars source, int period) : this(source.Close, period, false) { } - public SVAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public SVAR_Series(TSeries source) : this(source, 0, false) { } - public SVAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _svar = 0; - for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); } - _svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _svar); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/T3_Series.cs b/Calculations/_Updated/T3_Series.cs deleted file mode 100644 index 7cba0dcd..00000000 --- a/Calculations/_Updated/T3_Series.cs +++ /dev/null @@ -1,161 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Numerics; - -/* -T3: Tillson T3 Moving Average - Tim Tillson described it in "Technical Analysis of Stocks and Commodities", January 1998 in the - article "Better Moving Averages". Tillson’s moving average becomes a popular indicator of - technical analysis as it gets less lag with the price chart and its curve is considerably smoother. - -Sources: - https://technicalindicators.net/indicators-technical-analysis/150-t3-moving-average - http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/ - */ - -public class T3_Series : TSeries { - private readonly double _k, _k1m, _c1, _c2, _c3, _c4; - private readonly System.Collections.Generic.List _buffer1 = new(); - private readonly System.Collections.Generic.List _buffer2 = new(); - private readonly System.Collections.Generic.List _buffer3 = new(); - private readonly System.Collections.Generic.List _buffer4 = new(); - private readonly System.Collections.Generic.List _buffer5 = new(); - private readonly System.Collections.Generic.List _buffer6 = new(); - private readonly bool _useSMA; - private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6; - private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6; - protected int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) { - _period = period; - _len = 0; - _NaN = useNaN; - Name = $"T3({period})"; - _useSMA = useSMA; - double _a = vfactor; //0.7; //0.618 - _c1 = -_a * _a * _a; - _c2 = 3 * _a * _a + 3 * _a * _a * _a; - _c3 = -6 * _a * _a - 3 * _a - 3 * _a * _a * _a; - _c4 = 1 + 3 * _a + _a * _a * _a + 3 * _a * _a; - - _k = 2.0 / (_period + 1); - _k1m = 1.0 - _k; - _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0; - } - public T3_Series(TSeries source, int period, double vfactor, bool useSMA, bool useNaN) : this(period, vfactor, useSMA, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public T3_Series() : this(period: 0, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(int period) : this(period: period, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TBars source) : this(source.Close, 0, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TBars source, int period) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TBars source, int period, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { } - public T3_Series(TBars source, int period, double vfactor, bool useNaN) : this(source.Close, period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { } - public T3_Series(TBars source, int period, bool useSMA, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: useSMA, useNaN: useNaN) { } - public T3_Series(TSeries source) : this(source, 0, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TSeries source, int period) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: false) { } - public T3_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { } - public T3_Series(TSeries source, int period, double vfactor) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: false) { } - public T3_Series(TSeries source, int period, double vfactor, bool useNaN) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6; - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN),update); - } - - if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; _lastema4 = _llastema4; _lastema5 = _llastema5; _lastema6 = _llastema6; } - else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _llastema4 = _lastema4; _llastema5 = _lastema5; _llastema6 = _lastema6; } - - if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; } - - - if ((_len < _period) && _useSMA) { - BufferTrim(_buffer1, TValue.v, _period, update); - _ema1 = 0; - for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; } - _ema1 /= _buffer1.Count; - - BufferTrim(_buffer2, _ema1, _period, update); - _ema2 = 0; - for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; } - _ema2 /= _buffer2.Count; - - BufferTrim(_buffer3, _ema2, _period, update); - _ema3 = 0; - for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; } - _ema3 /= _buffer3.Count; - - BufferTrim(_buffer4, _ema3, _period, update); - _ema4 = 0; - for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; } - _ema4 /= _buffer4.Count; - - BufferTrim(_buffer5, _ema4, _period, update); - _ema5 = 0; - for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; } - _ema5 /= _buffer5.Count; - - BufferTrim(_buffer6, _ema5, _period, update); - _ema6 = 0; - for (int i = 0; i < _buffer6.Count; i++) { _ema6 += _buffer6[i]; } - _ema6 /= _buffer6.Count; - } - else { - _ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m); - _ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m); - _ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m); - _ema4 = (_ema3 * this._k) + (this._lastema4 * this._k1m); - _ema5 = (_ema4 * this._k) + (this._lastema5 * this._k1m); - _ema6 = (_ema5 * this._k) + (this._lastema6 * this._k1m); - } - _len++; - _lastema1 = _ema1; - _lastema2 = _ema2; - _lastema3 = _ema3; - _lastema4 = _ema4; - _lastema5 = _ema5; - _lastema6 = _ema6; - - double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _T3); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0; - _buffer1.Clear(); - _buffer2.Clear(); - _buffer3.Clear(); - _buffer4.Clear(); - _buffer5.Clear(); - _buffer6.Clear(); - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/TBars.cs b/Calculations/_Updated/TBars.cs deleted file mode 100644 index 0c0274d4..00000000 --- a/Calculations/_Updated/TBars.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace QuanTAlib; -using System; - -/* -TBars class - includes all series for common data used in indicators and other calculations. - Has a bit limited overloading and casting (compared to TSeries) - Includes Select(int) method to simplify choosing the most optimal data source for indicators - Includes the most basic pricing calcs: HL2, OC2, OHL3, HLC3, OHLC4, HLCC4 - (it is 'cheaper' to calculate them once during data capture than each time during data analysis) - - */ - -public class TBars : System.Collections.Generic.List<(DateTime t, double o, double h, double l, double c, double v)> -{ - public string Name { get; set; } - private readonly TSeries _open = new("open"); - private readonly TSeries _high = new("high"); - private readonly TSeries _low = new("low"); - private readonly TSeries _close = new("close"); - private readonly TSeries _volume = new("volume"); - private readonly TSeries _hl2 = new("HL2"); - private readonly TSeries _oc2 = new("OC2"); - private readonly TSeries _ohl3 = new("OHL3"); - private readonly TSeries _hlc3 = new("HLC3"); - private readonly TSeries _ohlc4 = new("OHLC4"); - private readonly TSeries _hlcc4 = new("HLCC4"); - - public TSeries Open => this._open; - public TSeries High => this._high; - public TSeries Low => this._low; - public TSeries Close => this._close; - public TSeries Volume => this._volume; - public TSeries HL2 => this._hl2; - public TSeries OC2 => this._oc2; - public TSeries OHL3 => this._ohl3; - public TSeries HLC3 => this._hlc3; - public TSeries OHLC4 => this._ohlc4; - public TSeries HLCC4 => this._hlcc4; - - public TBars() { } - - public TBars(string Name) { - this.Name = Name; - } - - public (DateTime t, double o, double h, double l, double c, double v) Last => this[^1]; - public TBars Tail(int count = 10) - { - TBars outBars = new(); - if (count > this.Count) { count = this.Count; } - for (int i = this.Count - count; i < this.Count; i++) { outBars.Add(this[i]); } - return outBars; - } - public TSeries Select(int source) - { - return source switch - { - 0 => _open, - 1 => _high, - 2 => _low, - 3 => _close, - 4 => _hl2, - 5 => _oc2, - 6 => _ohl3, - 7 => _hlc3, - 8 => _ohlc4, - _ => _hlcc4, - }; - } - public static string SelectStr(int source) - { - return source switch - { - 0 => "Open", - 1 => "High", - 2 => "Low", - 3 => "Close", - 4 => "HL2", - 5 => "OC2", - 6 => "OHL3", - 7 => "HLC3", - 8 => "OHLC4", - _ => "HLCC4", - }; - } - - public virtual (DateTime t, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) => - Add((t: (this.Count == 0) ? DateTime.Today : this[^1].t.AddDays(1),p.o,p.h,p.l,p.c,p.v),update); - - public virtual (DateTime t, double v) Add(double o, double h, double l, double c, double v, bool update = false) => - Add((o,h,l,c,v),update); - - public virtual (DateTime t, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) => - this.Add((t, o, h, l, c, v), update); - - public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - if (update) { this[^1] = TBar; } else { base.Add(TBar); } - - _open.Add((TBar.t, TBar.o), update); - _high.Add((TBar.t, TBar.h), update); - _low.Add((TBar.t, TBar.l), update); - _close.Add((TBar.t, TBar.c), update); - _volume.Add((TBar.t, TBar.v), update); - _hl2.Add((TBar.t, (TBar.h + TBar.l) * 0.5), update); - _oc2.Add((TBar.t, (TBar.o + TBar.c) * 0.5), update); - _ohl3.Add((TBar.t, (TBar.o + TBar.h + TBar.l) * 0.333333333333333), update); - _hlc3.Add((TBar.t, (TBar.h + TBar.l + TBar.c) * 0.333333333333333), update); - _ohlc4.Add((TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25), update); - _hlcc4.Add((TBar.t, (TBar.h + TBar.l + TBar.c + TBar.c) * 0.25), update); - - this.OnEvent(update); - return (TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25); - } - - public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); - public event NewDataEventHandler Pub; - protected virtual void OnEvent(bool update = false) { if (Pub != null && Pub.Target != this) { - Pub(this, new TSeriesEventArgs { update = update }); } } - - public void Sub(object source, TSeriesEventArgs e) { TBars ss = (TBars)source; if (ss.Count > 1) { - for (int i = 0; i < ss.Count; i++) { this.Add(ss[i]); } - } else { - this.Add(ss[^1], e.update); - } - } - - /// common helpers - public static void BufferTrim(System.Collections.Generic.List buffer, double value, int period, bool update) { - if (!update) { - buffer.Add(value); - if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); } - return; - } - buffer[^1] = value; - } - public virtual void Reset() { - } -} diff --git a/Calculations/_Updated/TEMA_Series.cs b/Calculations/_Updated/TEMA_Series.cs deleted file mode 100644 index f48019fa..00000000 --- a/Calculations/_Updated/TEMA_Series.cs +++ /dev/null @@ -1,120 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -TEMA: Triple Exponential Moving Average - TEMA uses EMA(EMA(EMA())) to calculate less laggy Exponential moving average. - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/ - -Remark: - ema1 = EMA(close, length) - ema2 = EMA(ema1, length) - ema3 = EMA(ema2, length) - TEMA = 3 * (ema1 - ema2) + ema3 - - */ - -public class TEMA_Series : TSeries { - private double _k; - private double _sum, _oldsum; - private double _lastema1, _oldema1, _lastema2, _oldema2, _lastema3, _oldema3; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - -//core constructor - public TEMA_Series(int period, bool useNaN, bool useSMA) { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"TEMA({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _sum = _oldsum = _lastema1 = _lastema2 = _lastema3 = 0; - } - public TEMA_Series() : this(0, false, true) {} - public TEMA_Series(int period) : this(period, false, true) {} - public TEMA_Series(TBars source) : this(source.Close, 0, false) {} - public TEMA_Series(TBars source, int period) : this(source.Close, period, false) {} - public TEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {} - public TEMA_Series(TSeries source, int period) : this(source, period, false, true) {} - public TEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {} - public TEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - -// core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (update) { - _lastema1 = _oldema1; - _lastema2 = _oldema2; - _lastema3 = _oldema3; - _sum = _oldsum; - } - else { - _oldema1 = _lastema1; - _oldema2 = _lastema2; - _oldema3 = _lastema3; - _oldsum = _sum; - _len++; - } - - if (_period == 0) { _k = 2.0 / (_len + 1); } - - double _ema1, _ema2, _ema3, _tema; - if (this.Count == 0) { - _ema1 = _ema2 = _ema3 =_sum = TValue.v; - } - else if (_len <= _period && _useSMA && _period != 0) { - _sum += TValue.v; - _ema1 = _sum / Math.Min(_len, _period); - _ema2 = _ema1; - _ema3 = _ema2; - } - else { - _ema1 = (TValue.v - _lastema1) * _k + _lastema1; - _ema2 = (_ema1 - _lastema2) * _k + _lastema2; - _ema3 = (_ema2 - _lastema3) * _k + _lastema3; - } - - _tema = (3 * (_ema1 - _ema2)) + _ema3; - - _lastema1 = Double.IsNaN(_ema1)?_lastema1:_ema1; - _lastema2 = Double.IsNaN(_ema2)?_lastema2:_ema2; - _lastema3 = Double.IsNaN(_ema3) ? _lastema3 : _ema3; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _tema); - return base.Add(res, update); - } - -//variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _sum = _oldsum = _lastema1 = _lastema2 = 0; - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/TRIMA_Series.cs b/Calculations/_Updated/TRIMA_Series.cs deleted file mode 100644 index 050a4b88..00000000 --- a/Calculations/_Updated/TRIMA_Series.cs +++ /dev/null @@ -1,84 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -TRIMA: Triangular Moving Average - A weighted moving average where the shape of the weights are triangular and the greatest - weight is in the middle of the period, - -Sources: - https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/ - -Remark: - trima = sma(sma(signal, n/2), n/2) - - */ - -public class TRIMA_Series : TSeries { - private readonly int _p1a, _p1b; - private readonly SMA_Series sma, trima; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public TRIMA_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"xMA({period})"; - _p1a = (int)Math.Floor((period * 0.5) + 1); - _p1b = (int)Math.Ceiling(0.5 * period); - sma = new(_p1a); - trima = new(_p1b); - - } - public TRIMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public TRIMA_Series() : this(period: 0, useNaN: false) { } - public TRIMA_Series(int period) : this(period: period, useNaN: false) { } - public TRIMA_Series(TBars source) : this(source.Close, 0, false) { } - public TRIMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public TRIMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public TRIMA_Series(TSeries source) : this(source, 0, false) { } - public TRIMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - - var _sma = sma.Add(TValue, update); - var _trima = trima.Add(_sma, update); - - var res = (_trima.t, Count < _period - 1 && _NaN ? double.NaN : _trima.v); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - sma.Reset(); - trima.Reset(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/TRIX_Series.cs b/Calculations/_Updated/TRIX_Series.cs deleted file mode 100644 index f955bf04..00000000 --- a/Calculations/_Updated/TRIX_Series.cs +++ /dev/null @@ -1,118 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -TRIX: Triple Exponential Average Oscillator - Developed by Jack Hutson in the early 1980s, the triple exponential average (TRIX) - has become a popular technical analysis tool to aid chartists in spotting diversions - and directional cues in stock trading patterns. - -Sources: - https://www.investopedia.com/terms/t/trix.asp - - */ - -public class TRIX_Series : TSeries { - private readonly double _k; - private readonly System.Collections.Generic.List _buffer1 = new(); - private readonly System.Collections.Generic.List _buffer2 = new(); - private readonly System.Collections.Generic.List _buffer3 = new(); - private double _lastema1, _lastema2, _lastema3; - private double _llastema1, _llastema2, _llastema3; - private int _len; - private readonly bool _useSMA; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - -//core constructors - - public TRIX_Series(int period, bool useNaN, bool useSMA) { - _period = period; - _NaN = useNaN; - _useSMA = useSMA; - Name = $"TRIX({period})"; - _k = 2.0 / (_period + 1); - _len = 0; - _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0; - } - public TRIX_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public TRIX_Series() : this(0, false, true) {} - public TRIX_Series(int period) : this(period, false, true) {} - public TRIX_Series(TBars source) : this(source.Close, 0, false) {} - public TRIX_Series(TBars source, int period) : this(source.Close, period, false) {} - public TRIX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {} - public TRIX_Series(TSeries source, int period) : this(source, period, false, true) {} - public TRIX_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {} - - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (double.IsNaN(TValue.v)) { - return base.Add((TValue.t, Double.NaN), update); - } - if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; } - if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; } - else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _len++; - } - - double _ema1, _ema2, _ema3; - if ((this.Count < _period) && _useSMA) { - BufferTrim(_buffer1, TValue.v, _period, update); - _ema1 = 0; - for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; } - _ema1 /= _buffer1.Count; - - BufferTrim(_buffer2, _ema1, _period, update); - _ema2 = 0; - for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; } - _ema2 /= _buffer2.Count; - - BufferTrim(_buffer3, _ema2, _period, update); - _ema3 = 0; - for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; } - _ema3 /= _buffer3.Count; - } - else { - _ema1 = (TValue.v - _lastema1) * _k + _lastema1; - _ema2 = (_ema1 - _lastema2) * _k + _lastema2; - _ema3 = (_ema2 - _lastema3) * _k + _lastema3; - } - double _trix = 100 * (_ema3 - _lastema3) / _lastema3; - _lastema1 = _ema1; - _lastema2 = _ema2; - _lastema3 = _ema3; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _trix); - return base.Add(res, update); - } - -//variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _len = 0; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/TR_Series.cs b/Calculations/_Updated/TR_Series.cs deleted file mode 100644 index 9365d5f6..00000000 --- a/Calculations/_Updated/TR_Series.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -TR: True Range - True Range was introduced by J. Welles Wilder in his book New Concepts in Technical Trading Systems. - It measures the daily range plus any gap from the closing price of the preceding day. - -Calculation: - d1 = ABS(High - Low) - d2 = ABS(High - Previous close) - d3 = ABS(Previous close - Low) - TR = MAX(d1,d2,d3) - -Sources: - https://www.macroption.com/true-range/ - - */ - -public class TR_Series : TSeries { - protected readonly TBars _data; - private double _cm1, _cm1_o; - - //core constructors - public TR_Series() { - Name = $"TR()"; - _cm1 = _cm1_o = double.NaN; - } - public TR_Series(TBars source) { - _data = source; - Name = $"TR({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _cm1 = _cm1_o = double.NaN; - _data.Pub += Sub; - Add(data: _data); - } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - - if (update) { - _cm1 = _cm1_o; - } - else { - _cm1_o = _cm1; - } - - if (_cm1 is double.NaN) { - _cm1 = TBar.c; - } - - double d1 = Math.Abs(TBar.h - TBar.l); - double d2 = Math.Abs(_cm1 - TBar.h); - double d3 = Math.Abs(_cm1 - TBar.l); - _cm1 = TBar.c; - var ret = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); - return base.Add(ret, update); - - } - - public new void Add(TBars data) { - foreach (var item in data) { Add(item, false); } - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TBar: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TBar: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TBar: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _cm1 = _cm1_o = double.NaN; - } -} \ No newline at end of file diff --git a/Calculations/_Updated/TSeries.cs b/Calculations/_Updated/TSeries.cs deleted file mode 100644 index 4a8a6312..00000000 --- a/Calculations/_Updated/TSeries.cs +++ /dev/null @@ -1,117 +0,0 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data; -using System.Linq; - -/* -TSeries is the cornerstone of all QuanTAlib classes. - TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads - and other helpers that simplify usage of library. - Think of TSeries as an equivalent of Numpy array. - - - includes Length property (to mimic array's method) - - includes publishing and subscribing methods that attach to events - - */ -public class TSeriesEventArgs : EventArgs { - public bool update { get; set; } -} - -public class TSeries : List<(DateTime t, double v)> { - private readonly (DateTime t, double v) Default = (DateTime.MinValue, double.NaN); - public IEnumerable t => this.Select(item => item.t); - public IEnumerable v => this.Select(item => item.v); - public (DateTime t, double v) Last => Count > 0 ? this[^1] : Default; - - public int Length => Count; - public string Name { get; set; } - public int Keep = 0; - - public TSeries() { - this.Name = "data"; - } - - public TSeries(string Name) { - this.Name = Name; - } - - public virtual (DateTime t, double v) Add(double v, bool update = false) { - return Add((t: Count == 0 ? DateTime.Today : this[^1].t.AddDays(1), v), update); - } - - public virtual (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - if (update) { - this[^1] = TValue; - } - else { - base.Add(TValue); - } - - OnEvent(update); - return TValue; - } - - public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) { - if (update) { - this[this.Count - 1] = (TBar.t, TBar.c); - } - else { - base.Add((TBar.t, TBar.c)); - } - - OnEvent(update); - return (TBar.t, TBar.c); - } - - public virtual (DateTime t, double v) Add(TSeries data) { - foreach (var item in data) { Add(item); } - return data.Last; - } - - public virtual (DateTime t, double v) Add(TBars data) { - foreach (var item in data) { Add(item.c, false); } - return (data.Last.t, data.Last.c); - } - - public void Sub(object source, TSeriesEventArgs e) { - var data = (TSeries) source; - if (data == null) { return; } - foreach (var item in data) { Add(item); } - } - - public delegate void NewEventHandler(object source, TSeriesEventArgs args); - - public event NewEventHandler Pub; - - protected virtual void OnEvent(bool update = false) - { - if (Keep > 0) { - TrimToSize(keep:Keep); - } - Pub?.Invoke(this, new TSeriesEventArgs {update = update}); - } - - /// common helpers - public static void BufferTrim(List buffer, double value, int period, bool update) { - if (!update) { - buffer.Add(value); - if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); } - return; - } - buffer[^1] = value; - } - public virtual void Reset() { - } - - public void TrimToSize(int keep) { - if (keep >= this.Count) { - return; // No need to trim if the series is already smaller than or equal to n - } - - // Remove elements from the beginning of the list - int elementsToRemove = this.Count - keep; - RemoveRange(0, elementsToRemove); - } -} diff --git a/Calculations/_Updated/VAR_Series.cs b/Calculations/_Updated/VAR_Series.cs deleted file mode 100644 index fcd0caba..00000000 --- a/Calculations/_Updated/VAR_Series.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -VAR: Population Variance - Population variance without Bessel's correction - -Sources: - https://en.wikipedia.org/wiki/Variance - Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction - -Remark: - VAR (Population Variance) is also known as a biased Sample Variance. For unbiased - sample variance use SVAR instead. - - */ - -public class VAR_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public VAR_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"VAR({period})"; - } - public VAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public VAR_Series() : this(period: 0, useNaN: false) { } - public VAR_Series(int period) : this(period: period, useNaN: false) { } - public VAR_Series(TBars source) : this(source.Close, 0, false) { } - public VAR_Series(TBars source, int period) : this(source.Close, period, false) { } - public VAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public VAR_Series(TSeries source) : this(source, 0, false) { } - public VAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _pvar = 0; - for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _pvar /= this._buffer.Count; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pvar); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/WMAPE_Series.cs b/Calculations/_Updated/WMAPE_Series.cs deleted file mode 100644 index bbbdeee4..00000000 --- a/Calculations/_Updated/WMAPE_Series.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -WMAPE: Weighted Mean Absolute Percentage Error - Measures the size of the error in percentage terms. Improves problems with MAPE - when there are zero or close-to-zero values because there would be a division by zero - or values of MAPE tending to infinity. - -Sources: - https://en.wikipedia.org/wiki/WMAPE - - */ - -public class WMAPE_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public WMAPE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"WMAPE({period})"; - } - public WMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public WMAPE_Series() : this(period: 0, useNaN: false) { } - public WMAPE_Series(int period) : this(period: period, useNaN: false) { } - public WMAPE_Series(TBars source) : this(source.Close, 0, false) { } - public WMAPE_Series(TBars source, int period) : this(source.Close, period, false) { } - public WMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public WMAPE_Series(TSeries source) : this(source, 0, false) { } - public WMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - - double _sma = _buffer.Average(); - - double _div = 0; - double _wmape = 0; - for (int i = 0; i < _buffer.Count; i++) { - _wmape += Math.Abs(_buffer[i] - _sma); - _div += Math.Abs(_buffer[i]); - } - _wmape = (_div != 0) ? _wmape / _div : double.PositiveInfinity; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wmape); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/WMA_Series.cs b/Calculations/_Updated/WMA_Series.cs deleted file mode 100644 index f423e3ca..00000000 --- a/Calculations/_Updated/WMA_Series.cs +++ /dev/null @@ -1,104 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -/* -WMA: (linearly) Weighted Moving Average - The weights are linearly decreasing over the period and the most recent data has - the heaviest weight. - -Sources: - https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/weighted-moving-average-wma/ - https://www.technicalindicators.net/indicators-technical-analysis/83-moving-averages-simple-exponential-weighted - - */ - -public class WMA_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - private System.Collections.Generic.List _weights; - protected int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - protected int _len; - public int Len { - get { return _len; } - set { _len = value; } - } - - //core constructors - public WMA_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"WMA({period})"; - _len = 1; - _weights = CalculateWeights(_period); - } - public WMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public WMA_Series() : this(period: 0, useNaN: false) { } - public WMA_Series(int period) : this(period: period, useNaN: false) { } - public WMA_Series(TBars source) : this(source.Close, 0, false) { } - public WMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public WMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public WMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - if (_period == 0) { - _weights = CalculateWeights(_len); - _len++; - } - double _wma = 0; - double totalWeights = (_buffer.Count * (_buffer.Count + 1)) * 0.5; - object lockObj = new object(); - Parallel.For(0, _buffer.Count, i => - { - double temp = _buffer[i] * this._weights[i]; - lock (lockObj) { _wma += temp; } - }); - _wma /= totalWeights; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //calculating weights - private static List CalculateWeights(int period) { - List weights = new List(period); - for (int i = 0; i < period; i++) { - weights.Add(i + 1); - } - return weights; - } - - //reset calculation - public override void Reset() { - _len = 0; - _weights = CalculateWeights(_period); - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ZLEMA_Series.cs b/Calculations/_Updated/ZLEMA_Series.cs deleted file mode 100644 index 0f7cd56c..00000000 --- a/Calculations/_Updated/ZLEMA_Series.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -ZLEMA: Zero Lag Exponential Moving Average - The Zero lag exponential moving average (ZLEMA) indicator was created by John - Ehlers and Ric Way. - -The formula for a given N-Day period and for a given Data series is: - Lag = (Period-1)/2 - Ema Data = {Data+(Data-Data(Lag days ago)) - ZLEMA = EMA (EmaData,Period) - -Remark: - The idea is do a regular exponential moving average (EMA) calculation but on a - de-lagged data instead of doing it on the regular data. Data is de-lagged by - removing the data from "lag" days ago thus removing (or attempting to remove) - the cumulative lag effect of the moving average. - - */ - -public class ZLEMA_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly EMA_Series _ema; - - //core constructor - public ZLEMA_Series(int period, bool useNaN, bool useSMA) { - _period = period; - _NaN = useNaN; - Name = $"ZLEMA({period})"; - _len = 1; - _ema = new(period); - } - //generic constructors (source) - - public ZLEMA_Series() : this(0, false, true) { } - public ZLEMA_Series(int period) : this(period, false, true) { } - public ZLEMA_Series(TBars source) : this(source.Close, 0, false) { } - public ZLEMA_Series(TBars source, int period) : this(source.Close, period, false) { } - public ZLEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public ZLEMA_Series(TSeries source, int period) : this(source, period, false, true) { } - public ZLEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - public ZLEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - int _lag; - if (_period == 0) { - _lag = (int)((_len - 1) * 0.5); - _len++; - } - else { _lag = (int)((_period - 1) * 0.5); } - _lag = Math.Min(_lag, _buffer.Count - 1); - _lag = Math.Max(_lag, 0) + 1; - double _zlValue = 2 * TValue.v - _buffer[^_lag]; - double _zlema = _ema.Add((TValue.t, _zlValue), update).v; - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlema); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - _ema.Reset(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ZL_Series.cs b/Calculations/_Updated/ZL_Series.cs deleted file mode 100644 index 033088e4..00000000 --- a/Calculations/_Updated/ZL_Series.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace QuanTAlib; - -using System; -using System.Linq; - -/* -ZL: Zero Lag - Data is de-lagged by removing the data from “lag” days ago, thus removing - (or attempting to) the cumulative effect of the moving average. - -Calculation: - Lag = (Period-1)/2 - ZL = Data + (Data - Data(Lag days ago) ) - -Sources: - https://mudrex.com/blog/zero-lag-ema-trading-strategy/ - - */ - -public class ZL_Series: TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - private int _len; - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly EMA_Series _ema; - - //core constructor - public ZL_Series(int period, bool useNaN, bool useSMA) { - _period = period; - _NaN = useNaN; - Name = $"ZL({period})"; - _len = 1; - _ema = new(period); - } - //generic constructors (source) - - public ZL_Series() : this(0, false, true) { } - public ZL_Series(int period) : this(period, false, true) { } - public ZL_Series(TBars source) : this(source.Close, 0, false) { } - public ZL_Series(TBars source, int period) : this(source.Close, period, false) { } - public ZL_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public ZL_Series(TSeries source, int period) : this(source, period, false, true) { } - public ZL_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } - public ZL_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); - int _lag; - if (_period == 0) { - _lag = (int)((_len - 1) * 0.5); - _len++; - } - else { _lag = (int)((_period - 1) * 0.5); } - _lag = Math.Min(_lag, _buffer.Count - 1); - _lag = Math.Max(_lag, 0) + 1; - double _zlValue = 2 * TValue.v - _buffer[^_lag]; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlValue); - return base.Add(res, update); - } - - //variation of Add() - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - _ema.Reset(); - } -} \ No newline at end of file diff --git a/Calculations/_Updated/ZSCORE_Series.cs b/Calculations/_Updated/ZSCORE_Series.cs deleted file mode 100644 index 06c37130..00000000 --- a/Calculations/_Updated/ZSCORE_Series.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Linq; - -namespace QuanTAlib; -using System; -using System.Collections.Generic; - -/* -ZSCORE: number of standard deviations from SMA - Z-score describes a value's relationship to the mean of a series, as measured in - terms of standard deviations from the mean. If a Z-score is 0, it indicates that - the data point's score is identical to the mean score. A Z-score of 1.0 would - indicate a value that is one standard deviation from the mean. Z-scores may be - positive or negative, with a positive value indicating the score is above the - mean and a negative score indicating it is below the mean. - -Sources: - https://en.wikipedia.org/wiki/Z-score - https://www.investopedia.com/terms/z/zscore.asp - -Calculation: - std = std * STDEV(close, length) - mean = SMA(close, length) - ZSCORE = (close - mean) / std - - */ - -public class ZSCORE_Series : TSeries { - private readonly System.Collections.Generic.List _buffer = new(); - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - - //core constructors - public ZSCORE_Series(int period, bool useNaN) { - _period = period; - _NaN = useNaN; - Name = $"ZSCORE({period})"; - } - public ZSCORE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public ZSCORE_Series() : this(period: 0, useNaN: false) { } - public ZSCORE_Series(int period) : this(period: period, useNaN: false) { } - public ZSCORE_Series(TBars source) : this(source.Close, 0, false) { } - public ZSCORE_Series(TBars source, int period) : this(source.Close, period, false) { } - public ZSCORE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } - public ZSCORE_Series(TSeries source) : this(source, 0, false) { } - public ZSCORE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { - BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update); - double _sma = _buffer.Average(); - - double _pvar = 0; - for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } - _pvar /= this._buffer.Count; - double _psdev = Math.Sqrt(_pvar); - double _zscore = (_psdev == 0) ? 1 : (TValue.v - _sma) / _psdev; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zscore); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() { - _buffer.Clear(); - } -} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..5d40b927 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,37 @@ + + + en-US + net8.0 + enable + enable + true + preview + false + false + true + AnyCPU + False + bin\$(Configuration)\ + False + full + true + true + true + snupkg + AnyCPU + true + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + D:\Quantower + $([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0]) + + + \ No newline at end of file diff --git a/GitVersion.yml b/GitVersion.yml index 42d649b0..ffbb8c15 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -3,28 +3,21 @@ branches: main: regex: ^main$ is-release-branch: true - is-mainline: true - prevent-increment-of-merged-branch-version: true track-merge-target: false + tracks-release-branches: false increment: Patch - source-branches: [ 'develop' ] - is-release-branch: true - is-mainline: true - label: '' - develop: - regex: ^dev$ - increment: Patch - is-release-branch: false - prevent-increment-of-merged-branch-version: false + source-branches: ['dev'] + dev: + regex: ^dev(elop)?(ment)?$ + increment: Inherit track-merge-target: true source-branches: [] - is-release-branch: false tracks-release-branches: true - is-mainline: false label: dev ignore: sha: [] -major-version-bump-message: '\+semver:\s?(feature|major)' -minor-version-bump-message: '\+semver:\s?(new|update|minor|add)' -no-bump-message: '\+semver:\s?(skip|none|fix)' - +major-version-bump-message: '\+semver:\s?(breaking|major)' +minor-version-bump-message: '\+semver:\s?(feature|minor)' +patch-version-bump-message: '\+semver:\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' +commit-message-incrementing: Enabled \ No newline at end of file diff --git a/Indicators/Charts/2MACross_chart.cs b/Indicators/Charts/2MACross_chart.cs deleted file mode 100644 index 58a8063d..00000000 --- a/Indicators/Charts/2MACross_chart.cs +++ /dev/null @@ -1,284 +0,0 @@ -using System; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class MovingAverage_chart : Indicator { - #region Parameters - [InputParameter("MA1: Type:", 0, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA1type = 15; - - [InputParameter("MA1: Smoothing period:", 1, 1, 999, 1, 1)] - private int MA1Period = 10; - - [InputParameter("MA1: Data source:", 2, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA1DataSource = 3; - - [InputParameter("MA2: Type:", 3, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA2type = 16; - - [InputParameter("MA2: Smoothing period:", 4, 1, 999, 1, 1)] - private int MA2Period = 50; - - [InputParameter("MA2: Data source:", 5, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA2DataSource = 8; - - [InputParameter("Long trades", 6)] - private bool LongTrades = true; - - [InputParameter("Short trades", 6)] - private bool ShortTrades = true; - - #endregion Parameters - - protected HistoricalData History; - private TBars bars; - - /////// - private TSeries MA1, MA2; - private CROSS_Series trades; - private COMPARE_Series overunder; - - /////// - - public MovingAverage_chart() { - this.SeparateWindow = false; - this.Name = "MAs Crossover"; - this.AddLineSeries("MA1", Color.LimeGreen, 2, LineStyle.Solid); - this.AddLineSeries("MA2", Color.OrangeRed, 2, LineStyle.Solid); - } - - protected override void OnInit() { - this.bars = new(); - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - for (int i = this.History.Count - 1; i >= 0; i--) { - var rec = this.History[i, SeekOriginHistory.Begin]; - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - this.Name = "MAs Cross: [ "; - switch (MA1type) { - case 0: - MA1 = new SMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA1 = new EMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA1 = new WMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA1 = new T3_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA1 = new SMMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA1 = new TRIMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA1 = new DWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period); - this.Name += $"FWMA"; - break; - case 8: - MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA1 = new TEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA1 = new ALMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA1 = new HMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA1 = new HEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA1Period); - MA1 = new MAMA_Series(source: bars.Select(this.MA1DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA1 = new KAMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA1 = new ZLEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA1 = new JMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"JMA"; - break; - } - - this.Name = this.Name + $" ({MA1Period}:{TBars.SelectStr(this.MA1DataSource)}) : "; - - switch (MA2type) { - case 0: - MA2 = new SMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA2 = new EMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA2 = new WMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA2 = new T3_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA2 = new SMMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA2 = new TRIMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA2 = new DWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period); - this.Name += $"FWMA"; - break; - case 8: - MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA2 = new TEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA2 = new ALMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA2 = new HMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA2 = new HEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA2Period); - MA2 = new MAMA_Series(source: bars.Select(this.MA2DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA2 = new KAMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA2 = new ZLEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA2 = new JMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"JMA"; - break; - } - this.Name += $"({MA2Period}:{TBars.SelectStr(this.MA2DataSource)}) ]"; - - int maxKeep = Math.Max(Math.Max(this.MA1Period, this.MA2Period), 100); - MA1.Keep = maxKeep; - MA2.Keep = maxKeep; - trades.Keep = maxKeep; - overunder.Keep = maxKeep; - - overunder = new(MA1, MA2); - trades = new(MA1, MA2); - } - - protected override void OnUpdate(UpdateArgs args) { - bool update = !(args.Reason == UpdateReason.NewBar || - args.Reason == UpdateReason.HistoricalBar); - this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), - this.GetPrice(PriceType.High), - this.GetPrice(PriceType.Low), - this.GetPrice(PriceType.Close), - this.GetPrice(PriceType.Volume), update); - this.SetValue(this.MA1[^1].v, lineIndex: 0); - this.SetValue(this.MA2[^1].v, lineIndex: 1); - - if (trades[^1].v == 1) { - this.EndCloud(0, 1, Color.Empty); - if (LongTrades) { - this.LinesSeries[0].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow)); - this.BeginCloud(0, 1, Color.FromArgb(127, Color.Green)); - } - if (ShortTrades) { - this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow)); - } - } - if (trades[^1].v == -1) { - this.EndCloud(0, 1, Color.Empty); - if (ShortTrades) { - this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow)); - this.BeginCloud(0, 1, Color.FromArgb(127, Color.Red)); - } - if (LongTrades) { - this.LinesSeries[0].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); - } - } - } - public override void OnPaintChart(PaintChartEventArgs args) { - base.OnPaintChart(args); - if (this.CurrentChart == null) {return;} - Graphics graphics = args.Graphics; - var mainWindow = this.CurrentChart.MainWindow; - int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); - int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); - int historycount = HistoricalData.Count; - int ymax = mainWindow.ClientRectangle.Height; - int xmax = mainWindow.ClientRectangle.Width; - - /* - for (int i = leftIndex; i <= rightIndex; i++) { - int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); - int width = this.CurrentChart.BarsWidth; - int height = (int)((equity[i+historycount].v) *proportion); - - Brush bb = Brushes.DarkSlateGray; - bb = (overunder[i+historycount].v>0 && LongTrades)? Brushes.Green : bb; - bb = (overunder[i + historycount].v < 0 && ShortTrades) ? Brushes.Red : bb; - - graphics.FillRectangle(bb, xi, ymax - height, width, height); - } - */ - } -} diff --git a/Indicators/Charts/2MASlope_chart.cs b/Indicators/Charts/2MASlope_chart.cs deleted file mode 100644 index 65acfa9c..00000000 --- a/Indicators/Charts/2MASlope_chart.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class MovingAverageSlope_chart : Indicator { - #region Parameters - [InputParameter("MA1: Type:", 0, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA1type = 16; - - [InputParameter("MA1: Smoothing period:", 1, 1, 999, 1, 1)] - private int MA1Period = 10; - - [InputParameter("MA1: Data source:", 2, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA1DataSource = 3; - - [InputParameter("MA2: Type:", 3, variants: new object[] - { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, - "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] - private int MA2type = 6; - - [InputParameter("MA2: Smoothing period:", 4, 1, 999, 1, 1)] - private int MA2Period = 50; - - [InputParameter("MA2: Data source:", 5, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int MA2DataSource = 8; - - [InputParameter("Data required for slope calc:", 6, 2, 10, 1, 1)] - private int SlopePeriod = 3; - - [InputParameter("Long trades", 7)] - private bool LongTrades = true; - - [InputParameter("Short trades", 8)] - private bool ShortTrades; - - #endregion Parameters - - protected HistoricalData History; - private TBars bars; - - /////// - private TSeries MA1, MA2; - private SLOPE_Series sMA1, sMA2; - private CROSS_Series sig1, sig2; - - private bool inLong, inShort; - /////// - - public MovingAverageSlope_chart() { - this.SeparateWindow = false; - this.Name = "Slopes convergence"; - this.AddLineSeries("MA1", Color.DarkSlateGray, 2, LineStyle.Solid); - this.AddLineSeries("MA2", Color.DarkSlateGray, 2, LineStyle.Solid); - } - - protected override void OnInit() { - this.bars = new(); - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - for (int i = this.History.Count - 1; i >= 0; i--) { - var rec = this.History[i, SeekOriginHistory.Begin]; - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - this.Name = "Slopes convergence: [ "; - switch (MA1type) { - case 0: - MA1 = new SMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA1 = new EMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA1 = new WMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA1 = new T3_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA1 = new SMMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA1 = new TRIMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA1 = new DWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period); - this.Name += $"FWMA"; - break; - case 8: - MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA1 = new TEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA1 = new ALMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA1 = new HMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA1 = new HEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA1Period); - MA1 = new MAMA_Series(source: bars.Select(this.MA1DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA1 = new KAMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA1 = new ZLEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA1 = new JMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); - this.Name += $"JMA"; - break; - } - - this.Name = this.Name + $" ({MA1Period}:{TBars.SelectStr(this.MA1DataSource)}) : "; - - switch (MA2type) { - case 0: - MA2 = new SMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMA"; - break; - case 1: - MA2 = new EMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"EMA"; - break; - case 2: - MA2 = new WMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"WMA"; - break; - case 3: - MA2 = new T3_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"T3"; - break; - case 4: - MA2 = new SMMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"SMMA"; - break; - case 5: - MA2 = new TRIMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TRIMA"; - break; - case 6: - MA2 = new DWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DWMA"; - break; - case 7: - MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period); - this.Name += $"FWMA"; - break; - case 8: - MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"DEMA"; - break; - case 9: - MA2 = new TEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"TEMA"; - break; - case 10: - MA2 = new ALMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ALMA"; - break; - case 11: - MA2 = new HMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HMA"; - break; - case 12: - MA2 = new HEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"HEMA"; - break; - case 13: - double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA2Period); - MA2 = new MAMA_Series(source: bars.Select(this.MA2DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); - this.Name += $"MAMA"; - break; - case 14: - MA2 = new KAMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"KAMA"; - break; - case 15: - MA2 = new ZLEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"ZLEMA"; - break; - default: - MA2 = new JMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); - this.Name += $"JMA"; - break; - } - this.Name += $"({MA2Period}:{TBars.SelectStr(this.MA2DataSource)}) ]"; - - sMA1 = new(MA1, SlopePeriod); - sMA2 = new(MA2, SlopePeriod); - sig1 = new(sMA1, 0); - sig2 = new(sMA2, 0); - - int maxKeep = Math.Max(Math.Max(this.MA1Period, this.MA2Period), 100); - - MA1.Keep = maxKeep; - MA2.Keep = maxKeep; - sMA1.Keep = maxKeep; - sMA2.Keep = maxKeep; - sig1.Keep = maxKeep; - sig2.Keep = maxKeep; - } - - protected override void OnUpdate(UpdateArgs args) { - bool update = !(args.Reason == UpdateReason.NewBar || - args.Reason == UpdateReason.HistoricalBar); - this.bars.Add(this.Time(),this.Open(), this.High(), this.Low(), this.Close(), this.Volume(), update); - this.SetValue(this.MA1[^1].v, lineIndex: 0); - this.SetValue(this.MA2[^1].v, lineIndex: 1); - - Color s1Color= (this.sMA1[^1].v > 0)?Color.LimeGreen:Color.OrangeRed; - Color s2Color = (this.sMA2[^1].v > 0) ? Color.LimeGreen : Color.OrangeRed; - - this.LinesSeries[0].SetMarker(0,s1Color); - this.LinesSeries[1].SetMarker(0,s2Color); - - if (sig1[^1].v > 0 || sig2[^1].v > 0) { - if (sMA1[^1].v >= 0 && sMA2[^1].v >= 0 && LongTrades) - { - inLong = true; - this.BeginCloud(0, 1, Color.FromArgb(127, Color.DarkGreen)); - this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v)? 0 : 1 ].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow)); - } - else { - this.EndCloud(0, 1, Color.Empty); - if (inShort && this.Count > 1) - { - this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow)); - inShort = false; - } - } - } - - if (sig1[^1].v < 0 || sig2[^1].v < 0) { - if (sMA1[^1].v <= 0 && sMA2[^1].v <= 0 && ShortTrades) - { - inShort = true; - this.BeginCloud(0, 1, Color.FromArgb(100, Color.Red)); - this.LinesSeries[(this.MA1[^1].v > this.MA2[^1].v) ? 0 : 1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow)); - } - else { - this.EndCloud(0, 1, Color.Empty); - if (inLong && this.Count > 1) { - LinesSeries[(this.MA1[^1].v > this.MA2[^1].v)?1:0].SetMarker(1, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); - inLong = false; - } - } - } - } - public override void OnPaintChart(PaintChartEventArgs args) { - base.OnPaintChart(args); - if (this.CurrentChart == null) {return;} - Graphics graphics = args.Graphics; - var mainWindow = this.CurrentChart.MainWindow; - int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); - int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); -/* - int historycount = HistoricalData.Count; - int ymax = mainWindow.ClientRectangle.Height; - - - for (int i = leftIndex; i <= rightIndex; i++) { - int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); - int width = this.CurrentChart.BarsWidth; - int height = (int)((equity[i+historycount].v) *proportion); - - Brush bb = Brushes.DarkSlateGray; - bb = (overunder[i+historycount].v>0 && LongTrades)? Brushes.Green : bb; - bb = (overunder[i + historycount].v < 0 && ShortTrades) ? Brushes.Red : bb; - - graphics.FillRectangle(bb, xi, ymax - height, width, height); - } -*/ - } -} diff --git a/Indicators/Charts/JMA_chart.cs b/Indicators/Charts/JMA_chart.cs deleted file mode 100644 index 449f6965..00000000 --- a/Indicators/Charts/JMA_chart.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Diagnostics; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -using TradingPlatform.BusinessLayer.Chart; -namespace QuanTAlib; - -public class JMA_chart : Indicator { - #region Parameters - - [InputParameter("Data source", 0, variants: new object[] - { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, - "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] - private int DataSource = 3; - - [InputParameter("Smoothing period", 1, 1, 999, 1, 1)] - private int Period = 9; - - [InputParameter("Volatility short", 2, 3, 50, 1, 1)] - private int Vshort = 10; - - [InputParameter("Volatility long", 3, 20, 500, 1, 1)] - private int Vlong = 65; - - [InputParameter("Phase", 4, -100, 100, 1, 2)] - private double Jphase; - - #endregion Parameters - - /////// - private JMA_Series indicator; - /////// - - protected TBars bars; - protected IChartWindow mainWindow; - protected Graphics graphics; - protected int firstOnScreenBarIndex, lastOnScreenBarIndex; - protected HistoricalData History; - protected int HistPeriod; - public JMA_chart() { - Name = "JMA - Jurik Moving Avg"; - Description = "Jurik Moving Average description"; - AddLineSeries(lineName: "JMA", lineColor: Color.Yellow, lineWidth: 3,lineStyle: LineStyle.Solid); - SeparateWindow = false; - HistPeriod = Period; - } - - - protected override void OnInit() { - base.OnInit(); - bars = new(); - var dur1 = this.HistoricalData.FromTime; - var dur = this.HistoricalData.Period.Duration.TotalSeconds * (HistPeriod * 4); //seconds of two periods - - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - - for (int i = this.History.Count - 1; i >= 0; i--) { - - var rec = this.History[i, SeekOriginHistory.Begin]; - - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - - indicator = new(source: bars.Select(DataSource), period: Period, phase: Jphase, vshort: Vshort, vlong: Vlong, useNaN: true); - indicator.Keep = Math.Max(Period, 100); - } - - protected override void OnUpdate(UpdateArgs args) { - base.OnUpdate(args); - bars.Add(Time(), GetPrice(PriceType.Open), - GetPrice(PriceType.High), - GetPrice(PriceType.Low), - GetPrice(PriceType.Close), - GetPrice(PriceType.Volume), - update: !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar)); - - this.SetValue(indicator[^1].v, lineIndex: 0); - } - public override void OnPaintChart(PaintChartEventArgs args) { - base.OnPaintChart(args); - if (this.CurrentChart == null) { - return; - } - - graphics = args.Graphics; - mainWindow = this.CurrentChart.MainWindow; - - DateTime leftTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left); - DateTime rightTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right); - firstOnScreenBarIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(leftTime); - lastOnScreenBarIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(rightTime)); - } - -} diff --git a/Indicators/Charts/TrailingStop.cs b/Indicators/Charts/TrailingStop.cs deleted file mode 100644 index 50a1d4f7..00000000 --- a/Indicators/Charts/TrailingStop.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Diagnostics; -using System.Drawing; -using System.Linq; -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class TrailingStop_chart : Indicator { - #region Parameters - - [InputParameter("Period", 0, 1, 100, 1, 1)] - protected int _period = 30; - - [InputParameter("Factor", 1, 1, 100, 0.1, 1)] - protected double _factor = 10; - - [InputParameter("Long TS", 2)] - private bool _LongTS = true; - - [InputParameter("Short TS", 3)] - private bool _ShortTS = true; - - #endregion Parameters - - /////// - private HistoricalData History; - private TBars bars; - private ATR_Series _atr; - private double _tslineL, _ratchetL, _tslineS, _ratchetS; - - /////// - - public TrailingStop_chart() { - Name = $"ATR Trailing Stop"; - AddLineSeries(lineName: "TrailingATR Long", lineColor: Color.Yellow, lineWidth: 1,lineStyle: LineStyle.Dot); - AddLineSeries(lineName: "Ratchet Long", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); - - AddLineSeries(lineName: "TrailingATR Short", lineColor: Color.Yellow, lineWidth: 1, lineStyle: LineStyle.Dot); - AddLineSeries(lineName: "Ratchet Short", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); - - SeparateWindow = false; - } - - - protected override void OnInit() { - this.Name = $"Trailing Stop (ATR:{_period}, Mult:{_factor:f2})"; - this.bars = new(); - - this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); - for (int i = this.History.Count - 1; i >= 0; i--) { - var rec = this.History[i, SeekOriginHistory.Begin]; - bars.Add(rec.TimeLeft, rec[PriceType.Open], - rec[PriceType.High], rec[PriceType.Low], - rec[PriceType.Close], rec[PriceType.Volume]); - } - _atr = new(source: bars, _period, useNaN: true); - _ratchetL = Double.NegativeInfinity; - _ratchetS = Double.PositiveInfinity; - - this.LinesSeries[0].Visible = _LongTS; - this.LinesSeries[1].Visible = _LongTS; - this.LinesSeries[2].Visible = _ShortTS; - this.LinesSeries[3].Visible = _ShortTS; - } - - protected override void OnUpdate(UpdateArgs args) { - bool update = !(args.Reason == UpdateReason.NewBar || - args.Reason == UpdateReason.HistoricalBar); - this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), - this.GetPrice(PriceType.High), - this.GetPrice(PriceType.Low), - this.GetPrice(PriceType.Close), - this.GetPrice(PriceType.Volume), update); - - _tslineL = bars.High[^1].v - (_factor * _atr[^1].v); - _ratchetL = Math.Max(_tslineL,_ratchetL); - if (_ratchetL > bars.Low[^1].v) { - this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.Yellow, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); - _ratchetL = _tslineL; - } - - _tslineS = bars.High[^1].v + (_factor * _atr[^1].v); - _ratchetS = Math.Min(_tslineS, _ratchetS); - if (_ratchetS < bars.High[^1].v) { - this.LinesSeries[3].SetMarker(0, new IndicatorLineMarker(Color.Yellow, upperIcon: IndicatorLineMarkerIconType.UpArrow)); - _ratchetS = _tslineS; - } - - this.SetValue(_tslineL, lineIndex: 0); - this.SetValue(_ratchetL, lineIndex: 1); - this.SetValue(_tslineS, lineIndex: 2); - this.SetValue(_ratchetS, lineIndex: 3); - } -} - diff --git a/QuanTAlib.sln b/QuanTAlib.sln index d0b6ce59..44dd6e71 100644 --- a/QuanTAlib.sln +++ b/QuanTAlib.sln @@ -1,74 +1,51 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.2.32210.308 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calculations", "Calculations\Calculations.csproj", "{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{584E06A9-CEB4-476A-85CC-6A8FF3974AE2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{D85FEBB4-B651-466F-85CC-FD902378D4D2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Indicators", "Indicators\Indicators.csproj", "{43AD2D78-024C-4D96-A70B-915CF519965A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MovingAverages", "quantower\averages\Averages.csproj", "{32CC09CC-26E3-4FCE-8932-C0513C4AD766}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strategies", "Strategies\Strategies.csproj", "{FA526AF6-95BC-4AC0-8B46-A304FD06689D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{20B1B5F1-8C36-4668-B0AE-951C13AE197B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{47B6ACDB-F535-4FEB-9A0A-C427CAE8C28E}" - ProjectSection(SolutionItems) = preProject - docs\.nojekyll = docs\.nojekyll - docs\ALMA.md = docs\ALMA.md - docs\DEMA.md = docs\DEMA.md - docs\DWMA.md = docs\DWMA.md - docs\EMA.md = docs\EMA.md - docs\FMA.md = docs\FMA.md - docs\getting_started.ipynb = docs\getting_started.ipynb - docs\HEMA.md = docs\HEMA.md - docs\HMA.md = docs\HMA.md - docs\HWMA.md = docs\HWMA.md - docs\index.html = docs\index.html - docs\indicators.md = docs\indicators.md - docs\JMA.md = docs\JMA.md - docs\KAMA.md = docs\KAMA.md - docs\LICENSE = docs\LICENSE - docs\MAMA.md = docs\MAMA.md - docs\QA.md = docs\QA.md - docs\readme.md = docs\readme.md - docs\RMA.md = docs\RMA.md - docs\SMA.md = docs\SMA.md - docs\SMMA.md = docs\SMMA.md - docs\T3.md = docs\T3.md - docs\TEMA.md = docs\TEMA.md - docs\TRIMA.md = docs\TRIMA.md - docs\WMA.md = docs\WMA.md - docs\ZLEMA.md = docs\ZLEMA.md - docs\_sidebar.md = docs\_sidebar.md - EndProjectSection +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{A8D9AE68-24E3-476C-BB98-244541BB4B43}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{B6D3EB11-63B6-430F-B526-E1981B3D8214}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.Build.0 = Release|Any CPU - {283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Release|Any CPU.Build.0 = Release|Any CPU - {43AD2D78-024C-4D96-A70B-915CF519965A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {43AD2D78-024C-4D96-A70B-915CF519965A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {43AD2D78-024C-4D96-A70B-915CF519965A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {43AD2D78-024C-4D96-A70B-915CF519965A}.Release|Any CPU.Build.0 = Release|Any CPU - {FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {E5592DC2-0542-45B2-A0CF-C6B1EDC72B87} + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Release|Any CPU.Build.0 = Release|Any CPU + {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Release|Any CPU.Build.0 = Release|Any CPU + {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Release|Any CPU.Build.0 = Release|Any CPU + {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Release|Any CPU.Build.0 = Release|Any CPU + {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B6D3EB11-63B6-430F-B526-E1981B3D8214} = {A8D9AE68-24E3-476C-BB98-244541BB4B43} EndGlobalSection EndGlobal diff --git a/SyntheticVendor/SyntheticVendor.cs b/SyntheticVendor/SyntheticVendor.cs new file mode 100644 index 00000000..58ad336b --- /dev/null +++ b/SyntheticVendor/SyntheticVendor.cs @@ -0,0 +1,1133 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using TradingPlatform.BusinessLayer; +using TradingPlatform.BusinessLayer.Integration; + +namespace SyntheticVendorNamespace +{ + public class SyntheticVendor : Vendor + { + private readonly List exchanges; + private readonly List assets; + private readonly List symbols; + + public SyntheticVendor() + { + exchanges = new List + { + + //Spike, + //Impulse, + //Triangle, + //Sawtooth + //Sine + //Chirp + //White + //Gauss + //B + //HF + //Impulse+HF, + //Sawtooth+HF + //Sine+G + //Chirp+G + //Complex + //Market + + new MessageExchange { Id = "PU", ExchangeName = "1 Pulse" }, + new MessageExchange { Id = "WA", ExchangeName = "2 Wave" }, + new MessageExchange { Id = "MD", ExchangeName = "3 Modulation" }, + new MessageExchange { Id = "NO", ExchangeName = "4 Noise" }, + new MessageExchange { Id = "BR", ExchangeName = "5 Brownian" }, + new MessageExchange { Id = "QT", ExchangeName = "6 QuanTAlib" } + }; + + assets = new List + { + new MessageAsset { Id = "USD", Name = "USD" }, + + }; + + symbols = new List + { + CreateMessageSymbol(id: "W1", name: "1 Digital spike", exchangeId: "QT", assetId: "USD", type: SymbolType.Crypto, + description: "Sudden sharp spike in the signal"), + CreateMessageSymbol("W2", "2 Dirac delta spike", "QT", "USD", SymbolType.Crypto), + CreateMessageSymbol("W8", "4 Sinc pulse", "QT", "USD", SymbolType.Crypto), + + CreateMessageSymbol("W3", "1 Square Wave", "QT", "USD", SymbolType.ETF), + CreateMessageSymbol("W4", "2 Sawtooth Wave", "QT", "USD", SymbolType.ETF), + CreateMessageSymbol("W5", "3 Inverse sawtooth Wave", "QT", "USD", SymbolType.ETF), + CreateMessageSymbol("W6", "4 Triangle Wave", "QT", "USD", SymbolType.ETF), + CreateMessageSymbol("W7", "5 Sine Wave", "QT", "USD", SymbolType.ETF), + + CreateMessageSymbol("W11", "1 Amplitude modulation", "QT", "USD", SymbolType.Forex), + CreateMessageSymbol("W10", "2 Frequency sweep", "QT", "USD", SymbolType.Forex), + CreateMessageSymbol("W12", "3 Frequency modulation", "QT", "USD", SymbolType.Forex), + + CreateMessageSymbol("W13", "1 White noise", "QT", "USD", SymbolType.Indexes), + CreateMessageSymbol("W14", "2 Pink noise", "QT", "USD", SymbolType.Indexes), + CreateMessageSymbol("W15", "3 Brown noise", "QT", "USD", SymbolType.Indexes), + + CreateMessageSymbol("W16", "1 Fractional Brownian motion", "QT", "USD", SymbolType.Synthetic), + CreateMessageSymbol("W17", "2 Geometric Brownian motion", "QT", "USD", SymbolType.Synthetic) + }; + +/* + Bond, + CFD, + Crypto, + Debentures, + Equities, + ETF, + FixedIncome, + Forex, + Forward, + Futures, + Indexes, + Options, + Spot, + Synthetic, + Swap, + Warrants, + +*/ + + + + } + + private MessageSymbol CreateMessageSymbol( + string id, + string name, + string exchangeId, + string assetId, + SymbolType type, + string description) + { + var messageSymbol = new MessageSymbol(id) + { + Name = name, + Description = description, + SymbolType = type, + ExchangeId = exchangeId, + ProductAssetId = assetId, + + // Setting some default values + QuotingCurrencyAssetID = "USD", + HistoryType = HistoryType.Last, + DeltaCalculationType = DeltaCalculationType.TickDirection, + LotSize = 1, + VariableTickList = new List + { + new VariableTick(0.01) // Default tick size + } + }; + + return messageSymbol; + } + + public static VendorMetaData GetVendorMetaData() + { + return new VendorMetaData() + { + VendorName = "Synthetic Vendor", + VendorDescription = "A synthetic vendor for testing and demonstration purposes", + GetDefaultConnections = () => + { + var defaultConnection = Vendor.CreateDefaultConnectionInfo( + "Synthetic Connection", + "Synthetic Vendor", + "", // Replace with actual path if you have a logo + allowCreateCustomConnections: true + ); + return new List { defaultConnection }; + } + }; + } + + + private MessageSymbol CreateMessageSymbol(string id, string name, string exchangeId, string assetId, SymbolType type) + { + return new MessageSymbol(id) + { + Name = name, + ExchangeId = exchangeId, + ProductAssetId = assetId, + QuotingCurrencyAssetID = "USD", + QuotingType = SymbolQuotingType.LotSize, + LotSize = 1, + NettingType = NettingType.OnePosition, + VolumeType = SymbolVolumeType.Volume, + AllowCalculateRealtimeTicks = true, + AllowCalculateRealtimeTrades = false, + AllowCalculateRealtimeVolume = true, + AllowCalculateRealtimeChange = true, + AllowAbbreviatePriceByTickSize = false, + NotionalValueStep = 0.01, + DeltaCalculationType = DeltaCalculationType.AggressorFlag, // Changed from None to AggressorFlag + MinVolumeAnalysisTickSize = 0.01, + MaturityDate = DateTime.MaxValue, // Set to max value for non-expiring symbols + HistoryType = HistoryType.Last, + MinLot = 0.01, + LotStep = 0.01, + MaxLot = 1000000, + SymbolType = type +/* + SymbolType.Unknown, + [EnumMember] Forex, + [EnumMember] Equities, + [EnumMember] CFD, + [EnumMember] Indexes, + [EnumMember] Futures, + [EnumMember] Options, + [EnumMember] ETF, + [EnumMember] Crypto, + [EnumMember] Synthetic, + [EnumMember] Spot, + [EnumMember] Forward, + [EnumMember] FixedIncome, + [EnumMember] Warrants, + + [EnumMember] Debentures, + [EnumMember] Bond, + [EnumMember] Swap, +*/ + }; + } + + public override ConnectionResult Connect(ConnectRequestParameters connectRequestParameters) + { + // Simulating connection process + Thread.Sleep(100); // Simulate some connection delay + + return ConnectionResult.CreateSuccess("Successfully connected to Synthetic Vendor"); + } + + public override void Disconnect() + { + // Simulating disconnection process + Thread.Sleep(500); // Simulate some disconnection delay + + } + + public override PingResult Ping() + { + return new PingResult() + { + State = PingEnum.Connected, + PingTime = TimeSpan.FromMilliseconds(2), + RoundTripTime = TimeSpan.FromMilliseconds(2) + }; + } + + + + + public override void OnConnected(CancellationToken token) + { + // This method is called after a successful connection + // You can initialize resources or start any necessary processes here + base.OnConnected(token); + + // For example, you might want to push some initial messages or data + // PushMessage(new MessageVendorEvent("SyntheticVendor connected successfully")); + } + + + + public override IList GetExchanges(CancellationToken token) + { + return exchanges; + } + + public override IList GetAssets(CancellationToken token) + { + return assets; + } + + public override IList GetSymbols(CancellationToken token) + { + return symbols; + } + + public override void SubscribeSymbol(SubscribeQuotesParameters parameters) + { + // Empty method for data subscription to be filled later + } + + public override void UnSubscribeSymbol(SubscribeQuotesParameters parameters) + { + // Empty method for data unsubscription to be filled later + } + + + public override IList LoadHistory(HistoryRequestParameters requestParameters) + { + var historyItems = new List(); + var symbolId = requestParameters.SymbolId; + + if (string.IsNullOrEmpty(symbolId)) return historyItems; + + DateTime from = requestParameters.FromTime; + DateTime to = requestParameters.ToTime; + + TimeSpan periodTimeSpan = requestParameters.Aggregation.GetPeriod.Duration; + + // Define the maximum number of items to generate per request + const int MAX_ITEMS_PER_REQUEST = 10000; + + Func waveGenerator = GetWaveGenerator(symbolId); + + DateTime currentTime = from; + while (currentTime < to) + { + DateTime intervalEnd = currentTime.AddTicks(periodTimeSpan.Ticks * MAX_ITEMS_PER_REQUEST); + if (intervalEnd > to) + intervalEnd = to; + + while (currentTime <= intervalEnd) + { + var historyItem = waveGenerator(currentTime, periodTimeSpan); //calling generator fuction + historyItems.Add(historyItem); + + currentTime = currentTime.Add(periodTimeSpan); + + if (requestParameters.CancellationToken.IsCancellationRequested) return historyItems; + } + + currentTime = intervalEnd; + } + + return historyItems; + } + + private Func GetWaveGenerator(string symbolId) + { + switch (symbolId) + { + //case "W0": return GenerateConstant; + case "W1": return GenerateSpike; + case "W2": return GenerateDiracDelta; + case "W3": return GenerateSquareWave; + case "W4": return GenerateSawtoothWave; + case "W5": return GenerateInverseSawtoothWave; + case "W6": return GenerateTriangleWave; + case "W7": return GenerateSineWave; + case "W8": return GenerateSincWave; + case "W9": return GenerateGaussianPulse; + case "W10": return GenerateFrequencySweep; + case "W11": return GenerateAMSignal; + case "W12": return GenerateFMSignal; + case "W13": return GenerateWhiteNoise; + case "W14": return GeneratePinkNoise; + case "W15": return GenerateBrownNoise; + case "W16": return GenerateFBM; + case "W17": return GenerateGBM; + + default: return GenerateSineWave; + } + } + + public override HistoryMetadata GetHistoryMetadata(CancellationToken cancellationToken) + { + return new HistoryMetadata() + { + AllowedHistoryTypes = new HistoryType[] + { + HistoryType.Bid, + HistoryType.Ask, + HistoryType.Midpoint, + HistoryType.Last, + HistoryType.BidAsk, + HistoryType.Mark, + }, + AllowedPeriods = new Period[] + { + Period.TICK1, + Period.SECOND1, Period.SECOND5, Period.SECOND10, Period.SECOND15, Period.SECOND30, + Period.MIN1, Period.MIN2, Period.MIN3, Period.MIN4, Period.MIN5, + Period.MIN10, Period.MIN15, Period.MIN30, + Period.HOUR1, Period.HOUR2, Period.HOUR3, Period.HOUR4, + Period.HOUR6, Period.HOUR8, Period.HOUR12, + Period.DAY1, + Period.WEEK1, + Period.MONTH1, + Period.YEAR1 + }, + UseHistoryLocalCache = false + }; + } + + +/*******************************************************************************************************************************************/ +/*******************************************************************************************************************************************/ +/*******************************************************************************************************************************************/ +/*******************************************************************************************************************************************/ +/*******************************************************************************************************************************************/ +/*******************************************************************************************************************************************/ +/*******************************************************************************************************************************************/ + + private HistoryItemBar GenerateSpike(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); + + // Calculate the number of hours since the epoch + double hoursSinceEpoch = (utcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalHours; + + // Calculate the position within the 25-hour cycle + int cyclePosition = (int)Math.Floor(hoursSinceEpoch % 25); + + // Determine if this is a spike hour (hour 24 in the cycle) or the hour after + bool isSpike = cyclePosition == 24; + bool isAfterSpike = cyclePosition == 0; + + double openValue, closeValue; + if (isSpike) + { + openValue = 0; + closeValue = 100; + } + else if (isAfterSpike) + { + openValue = 100; + closeValue = 0; + } + else + { + openValue = closeValue = 0.000001; + } + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = openValue, + High = Math.Max(openValue, closeValue), + Low = Math.Min(openValue, closeValue), + Close = closeValue, + Volume = Math.Abs(closeValue - openValue), + Ticks = time.Add(slice).Ticks - time.Ticks + }; + } + + + + + private static readonly double[] distributionValues = new double[] + { + 0.010, // Extreme left tail + 0.050, // Left tail + 0.200, // Left of center + 0.480, // Center (peak) + 0.200, // Right of center + 0.050, // Right tail + 0.010 // Extreme right tail + }; + + private HistoryItemBar GenerateDiracDelta(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); + + // Calculate the start of the current day + DateTime dayStart = utcTime.Date; + + // Determine which bar of the day we're on + int barOfDay = (int)((utcTime - dayStart).Ticks / slice.Ticks); + + double openValue, closeValue; + double scaleFactor = 100; // Scale factor to convert to percentage + + // Generate the spike pattern for the first 4 bars of each day + switch (barOfDay) + { + case 0: + openValue = 0.000001 * scaleFactor; + closeValue = 0.05 * scaleFactor; + break; + case 1: + openValue = 0.05 * scaleFactor; + closeValue = 0.50 * scaleFactor; + break; + case 2: + openValue = 0.50 * scaleFactor; + closeValue = 0.05 * scaleFactor; + break; + case 3: + openValue = 0.05 * scaleFactor; + closeValue = 0.0000001 * scaleFactor; + break; + default: + // Outside of the spike period, use baseline value + openValue = closeValue = 0.000001; + break; + } + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = openValue, + High = Math.Max(openValue, closeValue), + Low = Math.Min(openValue, closeValue), + Close = closeValue, + Volume = Math.Abs(closeValue - openValue), + Ticks = time.Add(slice).Ticks - time.Ticks + }; + } + + + + private HistoryItemBar GenerateSineWave(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); + + // Calculate the number of hours since the epoch + double minutesSinceEpoch = (utcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMinutes; + + // Calculate the position within the 25-hour cycle + double cyclePosition = minutesSinceEpoch % 1500; + + + // Calculate the sine wave values + double frequency = 2 * Math.PI / 1500; // Complete cycle over 25 hours + double value = 50 + 50 * Math.Sin(cyclePosition * frequency); // Oscillate between 0 and 100 + double nextValue = 50 + 50 * Math.Sin((cyclePosition + slice.TotalMinutes) * frequency); + + double factor = 0.6 * Math.Abs (nextValue - value); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + + High = Math.Max(value, nextValue)+factor, + Low = Math.Min(value, nextValue)-factor, + + Close = nextValue, + Volume = Math.Abs(nextValue - value) * 100, // Volume proportional to price change + Ticks = time.Add(slice).Ticks - time.Ticks + }; + } + + + private HistoryItemBar GenerateSquareWave(DateTime time, TimeSpan slice) + { + // Ensure we're working with UTC time + DateTime utcTime = time.ToUniversalTime(); + + // Calculate the time within the day (in hours) + double hoursInDay = utcTime.TimeOfDay.TotalHours; + + double openValue, closeValue; + + if (hoursInDay < 12) + { + // First half of the day + openValue = 99; + closeValue = 100; + } + else + { + // Second half of the day + openValue = 1; + closeValue = 0.0001; + } + + // Handle transition bars + if (Math.Abs(hoursInDay - 12) < slice.TotalHours / 2) + { + // Transition from 100 to 0 at noon + openValue = 100; + closeValue = 0.0001; + } + else if (hoursInDay < slice.TotalHours / 2 || hoursInDay > 24 - slice.TotalHours / 2) + { + // Transition from 0 to 100 at midnight + openValue = 0.0001; + closeValue = 100; + } + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = openValue, + High = Math.Max(openValue, closeValue), + Low = Math.Min(openValue, closeValue), + Close = closeValue, + Volume = Math.Abs(closeValue - openValue), + Ticks = time.Add(slice).Ticks - time.Ticks + }; + } + + private HistoryItemBar GenerateSawtoothWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; // 24-hour period + double position = hours % period; + double value = 200 * (position / period) - 100; + double nextValue = 200 * ((position + slice.TotalHours) % period / period) - 100; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GenerateInverseSawtoothWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; // 24-hour period + double position = hours % period; + double value = 100 - (200 * (position / period)); + double nextValue = 100 - (200 * ((position + slice.TotalHours) % period / period)); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GeneratePulseWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; // 24-hour period + double position = hours % period; + double value = position < period / 5 ? 100 : -100; // 20% duty cycle + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = 100, + Low = -100, + Close = value, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GenerateTriangleWave(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 24; + double position = hours % period; + double value = 200 * (Math.Abs(position / period - 0.5) - 0.25) * 100; + double nextValue = 200 * (Math.Abs(((position + slice.TotalHours) % period) / period - 0.5) - 0.25) * 100; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = 100, + Ticks = 100 + }; + } + + private HistoryItemBar GenerateSincWave(DateTime time, TimeSpan slice) + { + double minutes = (time - DateTime.UnixEpoch).TotalMinutes; + double period = 1500.0; // 24-hour period + double frequency = 2 * Math.PI / period; // Full cycle over 24 hours + + // Adjust time to center the main peak at 12 hours + double t = minutes % period - period / 2; + + // Scale factor + double scaleFactor = 7.0; + + // Calculate Sinc value + double x = scaleFactor * frequency * t; + double sincValue = x != 0 ? 100 * Math.Sin(x) / x : 100; + + // Calculate next value + double nextT = ((minutes + slice.TotalMinutes) % period) - period / 2; + double nextX = scaleFactor * frequency * nextT; + double nextSincValue = nextX != 0 ? 100 * Math.Sin(nextX) / nextX : 100; + + // Ensure minimum value + double minValue = 0.00001; + sincValue = Math.Sign(sincValue) * Math.Max(Math.Abs(sincValue), minValue); + nextSincValue = Math.Sign(nextSincValue) * Math.Max(Math.Abs(nextSincValue), minValue); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = sincValue, + High = Math.Max(sincValue, nextSincValue), + Low = Math.Min(sincValue, nextSincValue), + Close = nextSincValue, + Volume = Math.Abs(nextSincValue - sincValue), // Volume as the change in value + Ticks = slice.Ticks + }; + } + + private HistoryItemBar GenerateGaussianPulse(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double totalPeriod = 24.0; // 24-hour total cycle + double pulsePeriod = 12.0; // 12-hour pulse duration + double position = hours % totalPeriod; + + // Parameters for the Gaussian pulse + double amplitude = 100.0; // Maximum amplitude + double center = pulsePeriod / 2.0; // Center of the pulse (at 6 hours within the pulse period) + double width = pulsePeriod / 6.0; // Width of the pulse (adjusts the spread) + + double baselineValue = 0.00001; // Value outside the pulse period + + // Calculate the Gaussian pulse value + double value; + if (position < pulsePeriod) + { + value = amplitude * Math.Exp(-Math.Pow(position - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue; + } + else + { + value = baselineValue; + } + + // Calculate the next value for the slice + double nextPosition = (hours + slice.TotalHours) % totalPeriod; + double nextValue; + if (nextPosition < pulsePeriod) + { + nextValue = amplitude * Math.Exp(-Math.Pow(nextPosition - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue; + } + else + { + nextValue = baselineValue; + } + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = Math.Abs(nextValue - value), // Volume as the change in value + Ticks = slice.Ticks + }; + } + + private HistoryItemBar GenerateFrequencySweep(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double sweepPeriod = 48.0; // 48-hour period + + // Starting frequency (very low) + double minFreq = Math.PI / 48.0; + + // Calculate the ending frequency to ensure continuity + double maxFreq = Math.PI * 1.0 * Math.Exp(2 * Math.PI / sweepPeriod); + + // Calculate the exponential factor for frequency sweep + double expFactor = Math.Log(maxFreq / minFreq) / sweepPeriod; + + // Calculate the overall phase up to the current time + double totalPhase = (minFreq / expFactor) * (Math.Exp(expFactor * (hours % sweepPeriod)) - 1); + + // Shift the phase to start the cycle at 100 (cosine-like behavior) + totalPhase += Math.PI / 2; + + // Calculate the value of the signal at the current time + double value = 100.0 * Math.Sin(totalPhase); + + // Calculate the value of the signal at the end of the slice + double nextPhase = (minFreq / expFactor) * (Math.Exp(expFactor * ((hours + slice.TotalHours) % sweepPeriod)) - 1); + nextPhase += Math.PI / 2; // Apply the same phase shift + double nextValue = 100.0 * Math.Sin(nextPhase); + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, + Volume = Math.Abs(nextValue - value), // Volume as the change in value + Ticks = slice.Ticks + }; + } + + + Random random = new Random(); + private double currentAmplitude = 100; + private HistoryItemBar GenerateAMSignal(DateTime time, TimeSpan slice) + { + double hours = (time - DateTime.UnixEpoch).TotalHours; + double period = 12.0; + double frequency = 2 * Math.PI / period; // Frequency for a 5-hour period + + // Determine the start of the current 5-hour cycle + double cycleStartTime = Math.Floor(hours / period) * period; + + // Calculate the phase of the signal within the current 5-hour cycle + double phase = frequency * (hours % period); + + // If we're at the start of a new 5-hour cycle, generate a new amplitude + if (hours % period == 0) + { + currentAmplitude = random.NextDouble() * 100; + } + + // Calculate the value of the signal at the current time + double value = currentAmplitude * Math.Sin(phase); + + // Calculate the value of the signal at the end of the slice + double nextPhase = frequency * ((hours + slice.TotalHours) % period); + double nextValue = currentAmplitude * Math.Sin(nextPhase); + + // Create the HistoryItemBar + var historyItem = new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = value, + High = Math.Max(value, nextValue), + Low = Math.Min(value, nextValue), + Close = nextValue, // Set Close to the newly calculated value + Volume = Math.Abs(nextValue), // Volume as the change in value + Ticks = slice.Ticks + }; + + return historyItem; + } + + + private double currentFrequency = Math.PI / 220.0; // Initial frequency + private double accumulatedPhase = 0; + private double lastCloseValue = 0; // To store the last close value + + private HistoryItemBar GenerateFMSignal(DateTime time, TimeSpan slice) + { + double amplitude = 100.0; // Maximum amplitude + double minFreq = Math.PI / 256.0; + double maxFreq = Math.PI / 32.0; + + // Randomly adjust the frequency + double frequencyStep = (maxFreq - minFreq) * 0.2; // 20% of the frequency range + currentFrequency += (random.NextDouble() - 0.5) * 2 * frequencyStep; + currentFrequency = Math.Max(minFreq, Math.Min(maxFreq, currentFrequency)); // Clamp frequency + + // Calculate phase increment for this slice + double phaseIncrement = currentFrequency * slice.TotalHours; + + // Calculate the open value (which is the last close value) + double openValue = lastCloseValue; + + // Calculate the close value + accumulatedPhase += phaseIncrement; + double closeValue = amplitude * Math.Sin(2 * Math.PI * accumulatedPhase); + + // Determine high and low values + double midPhase = accumulatedPhase - (phaseIncrement / 2); + double midValue = amplitude * Math.Sin(2 * Math.PI * midPhase); + double highValue = Math.Max(Math.Max(openValue, closeValue), midValue); + double lowValue = Math.Min(Math.Min(openValue, closeValue), midValue); + + // Store the close value for the next iteration + lastCloseValue = closeValue; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = openValue, + High = highValue, + Low = lowValue, + Close = closeValue, + Volume = Math.Abs(closeValue - openValue), // Volume as the change in value + Ticks = slice.Ticks + }; + } + + + private HistoryItemBar GenerateWhiteNoise(DateTime time, TimeSpan slice) + { + double volatility = 2; + double meanReversionStrength = 0.1; + + double openNoise = random.NextDouble(); + double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose); + double closeNoise = random.NextDouble(); + double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open); + + // Determine High and Low + double high = Math.Max(open, close); + double low = Math.Min(open, close); + + // Add variation to High and Low + double highNoise = Math.Abs(random.NextDouble()); + high += volatility * highNoise; + + double lowNoise = Math.Abs(random.NextDouble()); + low -= volatility * lowNoise; + + double volume = Math.Abs(random.NextDouble()) * 1000 + 100; + + previousClose = close; + + + // Create the HistoryItemBar + var historyItem = new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + + return historyItem; + } + + + +private double previousClose = 50; +private const double meanPrice = 50; + +private HistoryItemBar GeneratePinkNoise(DateTime time, TimeSpan slice) +{ + double volatility = 2; + double meanReversionStrength = 0.1; + + // Generate open price + double openNoise = GeneratePinkNoiseValue(); + double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose); + + // Generate close price + double closeNoise = GeneratePinkNoiseValue(); + double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open); + + // Determine High and Low + double high = Math.Max(open, close); + double low = Math.Min(open, close); + + // Add variation to High and Low + double highNoise = Math.Abs(GeneratePinkNoiseValue()); + high += volatility * highNoise; + + double lowNoise = Math.Abs(GeneratePinkNoiseValue()); + low -= volatility * lowNoise; + + double volume = Math.Abs(GeneratePinkNoiseValue()) * 1000 + 100; + + // Update previous close for the next iteration + previousClose = close; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; +} + + + private const int NumOctaves = 6; + private double[] pinkNoiseState = new double[NumOctaves]; + private double GeneratePinkNoiseValue() + { + double total = 0; + + for (int i = 0; i < NumOctaves; i++) + { + double white = random.NextDouble() * 2 - 1; + pinkNoiseState[i] = (pinkNoiseState[i] + white) * 0.5; + total += pinkNoiseState[i] * Math.Pow(2, -i); + } + + // Normalize + return total / NumOctaves; + } + + + +private double lastValue = 0; + +private HistoryItemBar GenerateBrownNoise(DateTime time, TimeSpan slice) +{ + double dt = slice.TotalDays / 365.0; // Time step in years + double sigma = 25.0; // Annual volatility + + double increment = GenerateGaussian(0, sigma * Math.Sqrt(dt)); + double open = lastValue * (1 + GenerateGaussian(0, 0.05)); + double close = open + increment; + + // Simulate intra-period high and low + double high = Math.Max(open, close); + high += high * Math.Abs(GenerateGaussian(0, 0.06)); + double low = Math.Min(open, close); + low -= low * Math.Abs(GenerateGaussian(0, 0.06)); + + lastValue = close; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = Math.Abs(close - open) * 1000, // Simplified volume calculation + Ticks = slice.Ticks + }; +} +// Helper method to generate Gaussian distributed random numbers +private double GenerateGaussian(double mean, double stdDev) +{ + double u1 = 1.0 - random.NextDouble(); // Uniform(0,1] random doubles + double u2 = 1.0 - random.NextDouble(); + double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + return mean + stdDev * randStdNormal; +} + + + +private double GBMLastClose = 100; // Starting price +private double GBMMu = 0.05; // Annual drift +private double GBMSigma = 0.2; // Annual volatility + +private HistoryItemBar GenerateGBM(DateTime time, TimeSpan slice) +{ + // Convert time slice to years + double dt = slice.TotalDays / 365.0; + + // Generate a random normal variable for the main price movement + double epsilon = GenerateGaussian(0, 1); + + // Calculate the price movement using GBM equation + double drift = (GBMMu - 0.5 * GBMSigma * GBMSigma) * dt; + double diffusion = GBMSigma * Math.Sqrt(dt) * epsilon; + double returnValue = Math.Exp(drift + diffusion); + + // Add variability between previous close and current open + double openVariability = GBMLastClose * GBMSigma * Math.Sqrt(dt) * GenerateGaussian(0, 1) * 0.1; + double open = GBMLastClose + openVariability; + + // Calculate new close price + double close = open * returnValue; + + // Generate High and Low values + double highLowRange = Math.Max(Math.Abs(close - open), GBMLastClose * GBMSigma * Math.Sqrt(dt) * Math.Abs(GenerateGaussian(0, 1))); + double high = Math.Max(open, close) + highLowRange * 0.5; + double low = Math.Min(open, close) - highLowRange * 0.5; + + // Generate volume (you may want to adjust this based on your needs) + double volume = Math.Max(100, 1000 * Math.Abs(close - open) + 500 * GenerateGaussian(0, 1)); + + // Update last close for next iteration + GBMLastClose = close; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; +} + + private double FBMLastClose = 100; // Starting price + private double FBMHurst = 0.85; // Hurst parameter (0.5 < H < 1 for persistent fBm) + private double FBMSigma = 0.25; // Volatility parameter + private double FBMDrift = 0.001; // drift + + private HistoryItemBar GenerateFBM(DateTime time, TimeSpan slice) + { + double dt = Math.Pow(slice.TotalDays / 365.0, 0.5); + + double epsilon = GenerateFractionalGaussianNoise(FBMHurst); + + double drift = FBMDrift * dt; + double diffusion = FBMSigma * Math.Pow(dt, FBMHurst) * epsilon; + + double openVariability = FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * GenerateFractionalGaussianNoise(FBMHurst) * 0.1; + double open = FBMLastClose + openVariability; + + double close = open * Math.Exp(drift + diffusion); + + double highLowRange = Math.Max(Math.Abs(close - open), + FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst)) * 2); + double high = Math.Max(open, close) + highLowRange * 0.5; + double low = Math.Min(open, close) - highLowRange * 0.5; + + double volume = Math.Max(100, 2000 * Math.Abs(close - open) + + 1000 * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst))); + + FBMLastClose = close; + + return new HistoryItemBar + { + TicksLeft = time.Ticks, + TicksRight = time.Add(slice).Ticks - 1, + Open = open, + High = high, + Low = low, + Close = close, + Volume = volume, + Ticks = slice.Ticks + }; + } + + private double GenerateFractionalGaussianNoise(double hurst) + { + double sum = 0; + int n = 1000; // Number of terms in the approximation + + for (int i = 1; i <= n; i++) + { + double ri = GenerateGaussian(0, 1); + sum += (Math.Pow(i, hurst - 0.5) - Math.Pow(i - 1, hurst - 0.5)) * ri; + } + + return sum / Math.Sqrt(n); + } + + + + // Add other necessary overrides and implementations as needed + } +} \ No newline at end of file diff --git a/SyntheticVendor/SyntheticVendor.csproj b/SyntheticVendor/SyntheticVendor.csproj new file mode 100644 index 00000000..6703559f --- /dev/null +++ b/SyntheticVendor/SyntheticVendor.csproj @@ -0,0 +1,15 @@ + + + Vendor + + + + + ..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + + + + \ No newline at end of file diff --git a/Tests/Basic tests/Indicators.cs b/Tests/Basic tests/Indicators.cs deleted file mode 100644 index a9a9175e..00000000 --- a/Tests/Basic tests/Indicators.cs +++ /dev/null @@ -1,155 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Basics; -#nullable disable -public class Indicators -{ - private static Type[] maSeriesTypes = new Type[] - { - typeof(SMA_Series), - typeof(EMA_Series), - typeof(DEMA_Series), - typeof(TEMA_Series), - typeof(WMA_Series), - typeof(ALMA_Series), - typeof(DWMA_Series), - typeof(FWMA_Series), - typeof(HMA_Series), - typeof(ZLEMA_Series), - typeof(RMA_Series), - typeof(HEMA_Series), - typeof(JMA_Series), - typeof(CUSUM_Series), - typeof(SMMA_Series), - typeof(T3_Series), - typeof(KAMA_Series), - typeof(TRIMA_Series), - typeof(MAMA_Series), - typeof(HWMA_Series), -}; - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Name_exists(Type classType) - { - TSeries data = new("Data") {1,2,3}; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.NotEmpty(MA_Series.Name); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Series_Length(Type classType) - { - GBM_Feed feed = new(1000); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.Equal(1000, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Return_data(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var result = MA_Series.Add(20); - Assert.Equal(result.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Update(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var pre_update = MA_Series.Last.v; - - double pre_data = data.Last.v; - data.Add(20, true); - data.Add(pre_data, true); - - Assert.Equal(pre_update, MA_Series.Last.v); - Assert.Equal(data.Count, MA_Series.Count); -} - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_zero(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries; - Assert.Equal(data.Count, MA_Series.Count); - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Reset(Type classType) - { - GBM_Feed feed = new(10); - TSeries data = feed.OHLC4; - var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries; - MA_Series.Reset(); - data.Add(0); - Assert.Equal(data.Last.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_one(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries; - Assert.InRange(MA_Series.Last.v - data.Last.v, -10e-6, 10e-6); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void NaN_test(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.True(double.IsNaN(MA_Series[0].v)); - Assert.True(double.IsNaN(MA_Series[8].v)); - Assert.False(double.IsNaN(MA_Series[9].v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Edge_numbers(Type classType) - { - TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity }; - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.Equal(4, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void handling_NaN(Type classType) { - TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; -var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - -public static IEnumerable MASeriesData() - { - foreach (var type in maSeriesTypes) - { - yield return new object[] { type }; - } - } -} -#nullable restore \ No newline at end of file diff --git a/Tests/Basic tests/Oscillators.cs b/Tests/Basic tests/Oscillators.cs deleted file mode 100644 index 8ca89557..00000000 --- a/Tests/Basic tests/Oscillators.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Xunit; -using System; -using System.Runtime.InteropServices; -using QuanTAlib; - -namespace Basics; -#nullable disable -public class Oscillators -{ - private static Type[] maSeriesTypes = new[] - { - typeof(BIAS_Series), - typeof(MAX_Series), - typeof(MIN_Series), - typeof(MIDPOINT_Series), - typeof(ZL_Series), - typeof(DECAY_Series), - typeof(ENTROPY_Series), - typeof(KURTOSIS_Series), - typeof(MAD_Series), - typeof(MAPE_Series), - typeof(MAE_Series), - typeof(MSE_Series), - typeof(SDEV_Series), - typeof(SMAPE_Series), - typeof(WMAPE_Series), - typeof(SSDEV_Series), - typeof(VAR_Series), - typeof(SVAR_Series), - typeof(MEDIAN_Series), - typeof(ZSCORE_Series), - typeof(CMO_Series), - typeof(RSI_Series), - typeof(TRIX_Series), - typeof(BBANDS_Series), -}; - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Name_exists(Type classType) - { - TSeries data = new("Data") {1,2,3}; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.NotEmpty(MA_Series.Name); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Series_Length(Type classType) - { - GBM_Feed feed = new(1000); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - Assert.Equal(1000, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Return_data(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var result = MA_Series.Add(20); - Assert.Equal(result.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Update(Type classType) - { - TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; - var pre_update = MA_Series.Last.v; - - double pre_data = data.Last.v; - data.Add(20, true); - data.Add(pre_data, true); - - Assert.Equal(pre_update, MA_Series.Last.v); - Assert.Equal(data.Count, MA_Series.Count); -} - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_zero(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries; - Assert.Equal(data.Count, MA_Series.Count); - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Reset(Type classType) - { - GBM_Feed feed = new(10); - TSeries data = feed.OHLC4; - var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries; - MA_Series.Reset(); - data.Add(1); - Assert.False(double.IsNaN(MA_Series.Last.v)); -} - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_one(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries; - Assert.False(double.IsNaN(MA_Series[^1].v)); -} - - [Theory] - [MemberData(nameof(MASeriesData))] - public void NaN_test(Type classType) - { - GBM_Feed feed = new(100); - TSeries data = feed.OHLC4; - - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.True(double.IsNaN(MA_Series[0].v)); - Assert.True(double.IsNaN(MA_Series[8].v)); - Assert.False(double.IsNaN(MA_Series[9].v)); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Edge_numbers(Type classType) - { - TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity }; - var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.Equal(4, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void handling_NaN(Type classType) { - TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; -var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - -public static IEnumerable MASeriesData() - { - foreach (var type in maSeriesTypes) - { - yield return new object[] { type }; - } - } -} -#nullable restore \ No newline at end of file diff --git a/Tests/Basic tests/TBars_input.cs b/Tests/Basic tests/TBars_input.cs deleted file mode 100644 index 0b984821..00000000 --- a/Tests/Basic tests/TBars_input.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Xunit; -using System; -using System.Runtime.InteropServices; -using QuanTAlib; - -namespace Basics; -#nullable disable -public class TBars -{ - private static Type[] maSeriesTypes = new Type[] - { - typeof(ATR_Series), - typeof(ATRP_Series), - typeof(TR_Series), - typeof(ADL_Series), - typeof(CCI_Series), - typeof(OBV_Series), - typeof(ADOSC_Series), - typeof(MIDPRICE_Series), -}; - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Name_exists(Type classType) - { - GBM_Feed data = new(10); - - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - Assert.NotEmpty(MA_Series.Name); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Series_Length(Type classType) - { - GBM_Feed data = new(1000); - - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - Assert.Equal(1000, MA_Series.Count); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Return_data(Type classType) - { - GBM_Feed data = new(10); - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - var result = MA_Series.Add((DateTime.Today, 1,2,3,4,5)); - Assert.Equal(result.v, MA_Series.Last.v); - } - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Update(Type classType) - { - GBM_Feed data = new(10); - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - var pre_update = MA_Series.Last; - - var pre_data = data.Last; - data.Add((DateTime.Today, 1, 2, 3, 4, 5), true); - data.Add(pre_data, true); - - Assert.Equal(pre_update.v, MA_Series.Last.v); - Assert.Equal(data.Count, MA_Series.Count); -} - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Reset(Type classType) - { - GBM_Feed data = new(10); - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - MA_Series.Reset(); - data.Add(); - Assert.False(double.IsNaN(MA_Series.Last.v)); -} - - [Theory] - [MemberData(nameof(MASeriesData))] - public void Period_default(Type classType) { - GBM_Feed data = new(100); - - var MA_Series = Activator.CreateInstance(classType, data) as TSeries; - Assert.False(double.IsNaN(MA_Series.Last.v)); - } - - public static IEnumerable MASeriesData() - { - foreach (var type in maSeriesTypes) - { - yield return new object[] { type }; - } - } -} -#nullable restore \ No newline at end of file diff --git a/Tests/Pairs/DIV_Test.cs b/Tests/Pairs/DIV_Test.cs deleted file mode 100644 index d3e57573..00000000 --- a/Tests/Pairs/DIV_Test.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class DIV_Test -{ - [Fact] - public void DIVSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15 }; - TSeries b = new() { 5, 4, 3, 2, 1, 3 }; - DIV_Series c = new(a, b); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void DIVSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; - DIV_Series c = new(a, 0); - Assert.Equal(double.PositiveInfinity, c.Last().v); - } - - [Fact] - public void DIVDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 3.0 }; - DIV_Series c = new(12.0, a); - Assert.Equal(4.0, c.Last().v); - } - - [Fact] - public void DIVEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - DIV_Series c = new(a, b); - a.Add(12.0); - b.Add(2); - Assert.Equal(6.0, c.Last().v); - } - - [Fact] - public void DIVUpdatewDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15 }; - double b = 2; - DIV_Series c = new(a, b); - a.Add(10, true); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void DIVUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - DIV_Series c = new(a, b); - a.Add(10, true); - b.Add(2, true); - Assert.Equal(5, c.Last().v); - } -} diff --git a/Tests/Pairs/MUL_Test.cs b/Tests/Pairs/MUL_Test.cs deleted file mode 100644 index 22a343e5..00000000 --- a/Tests/Pairs/MUL_Test.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class MUL_Test -{ - [Fact] - public void MULSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - MUL_Series c = new(a, b); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void MULSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - MUL_Series c = new(a, 10.0); - Assert.Equal(50, c.Last().v); - } - - [Fact] - public void MULDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - MUL_Series c = new(5.0, a); - Assert.Equal(25, c.Last().v); - } - - [Fact] - public void MULEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - MUL_Series c = new(a, b); - a.Add(2); - b.Add(5); - Assert.Equal(10, c.Last().v); - } - - [Fact] - public void MULUpdateDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - double b = 10; - MUL_Series c = new(a, b); - a.Add(2, true); - Assert.Equal(20, c.Last().v); - } - - [Fact] - public void MULUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - MUL_Series c = new(a, b); - a.Add(10, true); - b.Add(10, true); - Assert.Equal(100, c.Last().v); - } -} diff --git a/Tests/Pairs/SUB_Test.cs b/Tests/Pairs/SUB_Test.cs deleted file mode 100644 index abed75f1..00000000 --- a/Tests/Pairs/SUB_Test.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class SUB_Test -{ - [Fact] - public void SUBSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - SUB_Series c = new(a, b); - Assert.Equal(4, c.Last().v); - } - - [Fact] - public void SUBSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; - SUB_Series c = new(a, 10.0); - Assert.Equal(5.0, c.Last().v); - } - - [Fact] - public void SUBDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; - SUB_Series c = new(10.0, a); - Assert.Equal(-5.0, c.Last().v); - } - - [Fact] - public void SUBEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - SUB_Series c = new(a, b); - a.Add(7.0); - b.Add(2); - Assert.Equal(5.0, c.Last().v); - } - - [Fact] - public void SUBUpdatewDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 15 }; - double b = 10; - SUB_Series c = new(a, b); - a.Add(1, true); - Assert.Equal(-9, c.Last().v); - } - - [Fact] - public void SUBUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 1 }; - SUB_Series c = new(a, b); - a.Add(10, true); - b.Add(0, true); - Assert.Equal(10, c.Last().v); - } -} diff --git a/Tests/Pairs/TBars_Test.cs b/Tests/Pairs/TBars_Test.cs deleted file mode 100644 index e23e10f7..00000000 --- a/Tests/Pairs/TBars_Test.cs +++ /dev/null @@ -1,112 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Bars; -public class TBars_Test -{ - [Fact] - public void InsertingTuple() - { - TBars s = new() { (t: DateTime.Today, o: double.Epsilon, h: double.NaN, l: Double.MaxValue, c: Double.NegativeInfinity, v: Double.PositiveInfinity) }; - var tup = (t: DateTime.Today, o: double.Epsilon, h: double.NaN, l: Double.MaxValue, - c: Double.NegativeInfinity, v: Double.PositiveInfinity); - Assert.Equal(tup, s[^1]); - } - - [Fact] - public void Casting_Parameters() - { - TBars s = new() - { - { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1, false } - }; - Assert.Equal(0.1, s[^1].o); - Assert.Equal(1.1, s[^1].h); - Assert.Equal(2.1, s[^1].l); - Assert.Equal(3.1, s[^1].c); - Assert.Equal(4.1, s[^1].v); - Assert.Equal(DateTime.Today, s[^1].t); - Assert.Single(s); - } - - [Fact] - public void Updating_Value() - { - TBars s = new() - { - { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1 } - }; - s.Add(DateTime.Today, 1.0, 1.0, 1.0, 1.0, 1.0, update: false); - s.Add(DateTime.Today, 0.0, 0.0, 0.0, 0.0, 0.0, update: true); - Assert.Equal(0.0, s[^1].o); - Assert.Equal(0.0, s[^1].h); - Assert.Equal(0.0, s[^1].l); - Assert.Equal(0.0, s[^1].c); - Assert.Equal(0.0, s[^1].v); - Assert.Equal(2, s.Count); - } - [Fact] - public void Extracting_TSeries() - { - TBars s = new() - { - { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1 }, - { DateTime.Today, 2.1, 3.1, 4.1, 5.1, 6.1 } - }; - - TSeries t = s.Open; - Assert.Equal(t.t, s.Open.t); - Assert.Equal(t.v, s.Open.v); - - t = s.High; - Assert.Equal(t.t, s.High.t); - Assert.Equal(t.v, s.High.v); - - t = s.Low; - Assert.Equal(t.t, s.Low.t); - Assert.Equal(t.v, s.Low.v); - - t = s.Close; - Assert.Equal(t.t, s.Close.t); - Assert.Equal(t.v, s.Close.v); - - t = s.Volume; - Assert.Equal(t.t, s.Volume.t); - Assert.Equal(t.v, s.Volume.v); - - t = s.HL2; - Assert.Equal(t.t, s.HL2.t); - Assert.Equal(t.v, s.HL2.v); - - t = s.OC2; - Assert.Equal(t.t, s.OC2.t); - Assert.Equal(t.v, s.OC2.v); - - t = s.OHL3; - Assert.Equal(t.t, s.OHL3.t); - Assert.Equal(t.v, s.OHL3.v); - - t = s.HLC3; - Assert.Equal(t.t, s.HLC3.t); - Assert.Equal(t.v, s.HLC3.v); - - t = s.OHLC4; - Assert.Equal(t.t, s.OHLC4.t); - Assert.Equal(t.v, s.OHLC4.v); - - t = s.HLCC4; - Assert.Equal(t.t, s.HLCC4.t); - Assert.Equal(t.v, s.HLCC4.v); - } - [Fact] - public void Broadcasting_Events() - { - TBars s = new() { (DateTime.Today, 2.1, 3.1, 4.1, 5.1, 6.1) }; - TSeries t = new(); - s.Close.Pub += t.Sub; - s.Add(DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1, false); - Assert.Equal(s.Close.v, t.v); - Assert.Equal(s.Close.Count, t.Count); - } -} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 8ec60052..0f30adf3 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,45 +1,35 @@ - + - net7.0 - preview - enable - enable - false - AnyCPU;x64 - 0.2.1.0 - 0.2.1.0 - 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d - 0.2.1-dev.2 - $(NoWarn);NETSDK1057 - true + net8.0 + QuanTAlib.Tests + QuanTAlib.Tests - - runtime; build; native; contentfiles; analyzers; buildtransitive + + all - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + + + + + + + + - - + + + - - - - - - - - - - + + \ No newline at end of file diff --git a/Tests/Validations/Trends/Pandas_TA.cs b/Tests/Validations/Trends/Pandas_TA.cs deleted file mode 100644 index f6953454..00000000 --- a/Tests/Validations/Trends/Pandas_TA.cs +++ /dev/null @@ -1,416 +0,0 @@ -using Xunit; -using System; -using QuanTAlib; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; -using Python.Runtime; - -namespace Validations; - -public class PandasTA : IDisposable { - private bool disposed = false; - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, skip; - private readonly int digits; - private readonly dynamic np; - private readonly dynamic ta; - private readonly dynamic pd; - private readonly dynamic df; - - public PandasTA() { - bars = new GBM_Feed(5000, 0.8, 0.0); - period = rnd.Next(28) + 3; - skip = period + 50; - digits = 8; - - var pythonDLL = PythonLibrary.Locate(); - Runtime.PythonDLL = pythonDLL; - PythonEngine.Initialize(); - - np = Py.Import("numpy"); - pd = Py.Import("pandas"); - ta = Py.Import("pandas_ta"); - - string[] cols = {"open", "high", "low", "close", "volume"}; - var ary = new double[bars.Count, 5]; - for (var i = 0; i < bars.Count; i++) { - ary[i, 0] = bars.Open[i].v; - ary[i, 1] = bars.High[i].v; - ary[i, 2] = bars.Low[i].v; - ary[i, 3] = bars.Close[i].v; - ary[i, 4] = bars.Volume[i].v; - } - - df = ta.DataFrame(data: np.array(ary), index: np.array(bars.Close.t), columns: np.array(cols)); - } - - public void Dispose() { - Dispose(true); - PythonEngine.Shutdown(); - GC.SuppressFinalize(this); - } - - ~PandasTA() { - Dispose(false); - } - - protected virtual void Dispose(bool disposing) { - if (!disposed) { - disposed = true; - } - } - - [Fact] - private void ADL() { - ADL_Series QL = new(bars); - var pta = df.ta.ad(high: df.high, low: df.low, close: df.close, volume: df.volume); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void BBANDS() { - BBANDS_Series QL = new(bars.Close, period); - var pta = df.ta.bbands(close: df.close, length: period).to_numpy(); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL.Lower[i].v; - var PanTA_item = (double) pta[i][0]; //lower - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Mid[i].v; - PanTA_item = (double) pta[i][1]; //mid - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Upper[i].v; - PanTA_item = (double) pta[i][2]; //upper - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void BIAS() { - BIAS_Series QL = new(bars.Close, period, false); - var pta = df.ta.bias(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void CCI() { - CCI_Series QL = new(bars, period, false); - var pta = df.ta.cci(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void DEMA() { - DEMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.dema(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void EMA() { - EMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.ema(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void ENTROPY() { - ENTROPY_Series QL = new(bars.Close, period, false); - var pta = df.ta.entropy(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void HL2() { - var pta = df.ta.hl2(high: df.high, low: df.low); - for (var i = bars.HL2.Length - 1; i > skip; i--) { - var QL_item = bars.HL2[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void HLC3() { - var pta = df.ta.hlc3(high: df.high, low: df.low, close: df.close); - for (var i = bars.HLC3.Length; i > skip; i--) { - var QL_item = bars.HLC3[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void HMA() { - HMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.hma(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void KURTOSIS() { - KURTOSIS_Series QL = new(bars.Close, period, false); - var pta = df.ta.kurtosis(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void MACD() { - MACD_Series QL = new(bars.Close, 26, 12, 9, false); - var pta = df.ta.macd(close: df.close).to_numpy(); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1][0]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Signal[i - 1].v; - PanTA_item = (double) pta[i - 1][2]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void MAD() { - MAD_Series QL = new(bars.Close, period, false); - var pta = df.ta.mad(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void MEDIAN() { - MEDIAN_Series QL = new(bars.Close, period); - var pta = df.ta.median(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void OBV() { - OBV_Series QL = new(bars); - var pta = df.ta.obv(close: df.close, volume: df.volume); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void OHLC4() { - var pta = df.ta.ohlc4(open: df.open, high: df.high, low: df.low, close: df.close); - for (var i = bars.OHLC4.Length; i > skip; i--) { - var QL_item = bars.OHLC4[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SDEV() { - SDEV_Series QL = new(bars.Close, period, false); - var pta = df.ta.stdev(close: df.close, length: period, ddof: 0); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SMA() { - SMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.sma(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SSDEV() { - SSDEV_Series QL = new(bars.Close, period, false); - var pta = df.ta.stdev(close: df.close, length: period, ddof: 1); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void SVARIANCE() { - SVAR_Series QL = new(bars.Close, period); - var pta = df.ta.variance(close: df.close, length: period, ddof: 1); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void TEMA() { - TEMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.tema(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void TR() { - TR_Series QL = new(bars); - var pta = df.ta.true_range(high: df.high, low: df.low, close: df.close); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void TRIMA() { - // TODO: return length to variable length (period) when Pandas-TA fixes trima to calculate even periods right - TRIMA_Series QL = new(bars.Close, 11); - var pta = df.ta.trima(close: df.close, length: 11); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void VARIANCE() { - VAR_Series QL = new(bars.Close, period); - var pta = df.ta.variance(close: df.close, length: period, ddof: 0); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void WMA() { - WMA_Series QL = new(bars.Close, period, false); - var pta = df.ta.wma(close: df.close, length: period); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - private void ZSCORE() { - ZSCORE_Series QL = new(bars.Close, period, false); - var pta = df.ta.zscore(close: df.close, length: period, ddof: 0); - for (var i = QL.Length - 1; i > skip; i--) { - var QL_item = QL[i - 1].v; - var PanTA_item = (double) pta[i - 1]; - Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } -} - -public static class PythonLibrary { - public static string Locate() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? Array.Empty(); - foreach (string path in paths) { - string[] pythonDLLs = Directory.GetFiles(path, "python3*.dll"); - if (pythonDLLs.Length > 0) { - foreach (string item in pythonDLLs) { - if (!item.EndsWith("python3.dll", StringComparison.OrdinalIgnoreCase)) { - return item; - } - } - - } - } - throw new FileNotFoundException("Python library not found in PATH"); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - return "/usr/lib/x86_64-linux-gnu/libpython3.10.so"; -/* - List pythonLibraries = new List(); - List directoriesToSearch = new List { "/home/runner/.local/lib" }; // Add more directories as needed - string filePattern = "libpython3.*.so"; - SearchFiles(directoriesToSearch, filePattern, pythonLibraries); - - if (pythonLibraries.Count > 0) { - return pythonLibraries[0]; - } - else { - throw new FileNotFoundException("Python library not found"); - } -*/ - } - - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - throw new NotSupportedException("Not supported yet"); - } - - else { throw new NotSupportedException("Unsupported operating system"); } - } - static void SearchFiles(List directoriesToSearch, string filePattern, List foundFiles) - { - foreach (string directory in directoriesToSearch) - { - if (Directory.Exists(directory)) - { - try - { - string[] files = Directory.GetFiles(directory, filePattern, SearchOption.AllDirectories); - foundFiles.AddRange(files); - } - catch (Exception e) - { - Console.WriteLine("Error searching in directory: " + directory + " - " + e.Message); - } - } - } - } -} \ No newline at end of file diff --git a/Tests/Validations/Trends/Skender.cs b/Tests/Validations/Trends/Skender.cs deleted file mode 100644 index a3950e96..00000000 --- a/Tests/Validations/Trends/Skender.cs +++ /dev/null @@ -1,486 +0,0 @@ -using System; -using QuanTAlib; -using Skender.Stock.Indicators; -using Xunit; - -namespace Validations; -public class Skender -{ - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, digits, skip; - private readonly IEnumerable quotes; - - - public Skender() - { - bars = new(Bars: 10000, Volatility: 0.5, Drift: 0.0, Precision: 2); - period = rnd.Next(30) + 5; - digits = 6; //minimizing rounding errors in type conversions - skip = period+2; - - quotes = bars.Select(q => new Quote - { - Date = q.t, - Open = (decimal)q.o, - High = (decimal)q.h, - Low = (decimal)q.l, - Close = (decimal)q.c, - Volume = (decimal)q.v - }); - } - -/* - [Fact] - public void ADL() - { - ADL_Series QL = new(bars); - var SK = quotes.GetAdl().Select(i => i.Adl); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1)!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - */ - [Fact] - public void ALMA() - { - ALMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetAlma(period).Select(i => i.Alma.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void ATR() - { - ATR_Series QL = new(bars, period:period,useNaN: false); - var SK = quotes.GetAtr(period).Select(i => i.Atr.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void ATRP() - { - ATRP_Series QL = new(bars, period, false); - var SK = quotes.GetAtr(period).Select(i => i.Atrp.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void BBANDS() - { - BBANDS_Series QL = new(bars.Close, period, 2.0, useNaN: false); - var SK = quotes.GetBollingerBands(period, 2.0); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL.Mid[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Sma!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Upper[i - 1].v; - SK_item = SK.ElementAt(i - 1).UpperBand!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Lower[i - 1].v; - SK_item = SK.ElementAt(i - 1).LowerBand!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Bandwidth[i - 1].v; - SK_item = SK.ElementAt(i - 1).Width!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.PercentB[i - 1].v; - SK_item = SK.ElementAt(i - 1).PercentB!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - QL_item = QL.Zscore[i - 1].v; - SK_item = SK.ElementAt(i - 1).ZScore!.Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void CCI() - { - CCI_Series QL = new(bars, period, false); - var SK = quotes.GetCci(period).Select(i => i.Cci.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void CMO() - { - CMO_Series QL = new(bars.Close, period, false); - var SK = quotes.GetCmo(period).Select(i => i.Cmo.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void CORR() - { - CORR_Series QL = new(bars.High, bars.Low, period, false); - var SK = quotes.Use(CandlePart.High).GetCorrelation(quotes.Use(CandlePart.Low), period).Select(i => i.Correlation.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void COVAR() - { - COVAR_Series QL = new(bars.High, bars.Low, period, false); - var SK = quotes.Use(CandlePart.High).GetCorrelation(quotes.Use(CandlePart.Low), period).Select(i => i.Covariance.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void DEMA() - { - DEMA_Series QL = new(bars.Close, period, false, useSMA: true); - var SK = quotes.GetDema(period).Select(i => i.Dema.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void EMA() - { - EMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetEma(lookbackPeriods: period).Select(i => i.Ema.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void HL2() - { - TSeries QL = bars.HL2; - var SK = quotes.GetBaseQuote(CandlePart.HL2).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void HLC3() - { - TSeries QL = bars.HLC3; - var SK = quotes.GetBaseQuote(CandlePart.HLC3).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void HMA() - { - HMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetHma(period).Select(i => i.Hma.Null2NaN()!); - for (int i = QL.Length; i > skip*2; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - - [Fact] - public void KAMA() - { - // TODO: check precision of KAMA() - KAMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetKama(period).Select(i => i.Kama.Null2NaN()!); - for (int i = QL.Length; i > skip+2; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void SLOPE() - { - SLOPE_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetSlope(period); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = (double)SK.ElementAt(i - 1).Slope!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - QL_item = QL.Intercept[i - 1].v; - SK_item = (double)SK.ElementAt(i - 1).Intercept!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - QL_item = QL.RSquared[i - 1].v; - SK_item = (double)SK.ElementAt(i - 1).RSquared!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - QL_item = QL.StdDev[i - 1].v; - SK_item = (double)SK.ElementAt(i - 1).StdDev!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void MACD() - { - MACD_Series QL = new(bars.Close, 26, 12, 9, useNaN: false); - var SK = quotes.GetMacd(12, 26, 9); - for (int i = QL.Length; i > 27; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Macd.Null2NaN()!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - //QL_item = QL.Signal[i - 1].v; - //SK_item = SK.ElementAt(i - 1).Signal.Null2NaN()!; - //Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void MAD() - { - MAD_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mad.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void MAMA() - { - MAMA_Series QL = new(bars.HL2, fastlimit: 0.5, slowlimit: 0.05); - var SK = quotes.GetMama(fastLimit: 0.5, slowLimit: 0.05); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Mama.Null2NaN()!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - QL_item = QL.Fama[i - 1].v; - SK_item = SK.ElementAt(i - 1).Fama.Null2NaN()!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void MAPE() - { - MAPE_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mape.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void MSE() - { - MSE_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mse.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void OBV() - { - OBV_Series QL = new(bars, period, false); - var SK = quotes.GetObv(period).Select(i => i.Obv!); - for (int i = QL.Length; i > skip; i--) { - double QL_item = QL.Last().v; - // adding volume[0] to OBV to pass the test and keep compatibility with TA-LIB - double SK_item = SK.Last()! + (double)quotes.First().Volume!; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void OC2() - { - TSeries QL = bars.OC2; - var SK = quotes.GetBaseQuote(CandlePart.OC2).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void OHL3() - { - TSeries QL = bars.OHL3; - var SK = quotes.GetBaseQuote(CandlePart.OHL3).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void OHLC4() - { - TSeries QL = bars.OHLC4; - var SK = quotes.GetBaseQuote(CandlePart.OHLC4).ToList(); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1).Value; - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void RSI() - { - RSI_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetRsi(period).Select(i => i.Rsi.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void SDEV() - { - SDEV_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetStdDev(period).Select(i => i.StdDev.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void SMA() - { - SMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetSma(period).Select(i => i.Sma.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void SMMA() - { - SMMA_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetSmma(period).Select(i => i.Smma.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void T3() - { - T3_Series QL = new(source: bars.Close, period: period, vfactor: 0.7, false); - var SK = quotes.GetT3(lookbackPeriods: period, volumeFactor: 0.7).Select(i => i.T3.Null2NaN()!); - for (int i = QL.Length; i > period*15; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void TRIX() { - TRIX_Series QL = new(bars.Close, period, false); - var SK = quotes.GetTrix(period).Select(i => i.Trix.Null2NaN()!); - for (int i = QL.Length; i > period*12; i--) { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); - } - } - [Fact] - public void TEMA() - { - TEMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetTema(period).Select(i => i.Tema.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void TR() - { - TR_Series QL = new(bars); - var SK = quotes.GetTr().Select(i => i.Tr.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void WMA() - { - WMA_Series QL = new(bars.Close, period, false); - var SK = quotes.GetWma(period).Select(i => i.Wma.Null2NaN()!); - for (int i = QL.Length; i > skip*2; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - [Fact] - public void ZSCORE() - { - ZSCORE_Series QL = new(bars.Close, period, useNaN: false); - var SK = quotes.GetStdDev(period).Select(i => i.ZScore.Null2NaN()!); - for (int i = QL.Length; i > skip; i--) - { - double QL_item = QL[i - 1].v; - double SK_item = SK.ElementAt(i - 1); - Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); - } - } - -} diff --git a/Tests/Validations/Trends/TA_LIB.cs b/Tests/Validations/Trends/TA_LIB.cs deleted file mode 100644 index 01e40c2a..00000000 --- a/Tests/Validations/Trends/TA_LIB.cs +++ /dev/null @@ -1,483 +0,0 @@ -using Xunit; -using System; -using TALib; -using QuanTAlib; - -namespace Validations; -public class Ta_Lib -{ - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, digits, skip; - private readonly double[] TALIB; - private readonly double[] TALIB2; - private readonly double[] inopen; - private readonly double[] inhigh; - private readonly double[] inlow; - private readonly double[] inclose; - private readonly double[] involume; - - public Ta_Lib() - { - bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3); - period = rnd.Next(28) + 3; - skip = period+2; - digits = 9; - - TALIB = new double[bars.Count]; - TALIB2 = new double[bars.Count]; - inopen = bars.Open.v.ToArray(); - inhigh = bars.High.v.ToArray(); - inlow = bars.Low.v.ToArray(); - inclose = bars.Close.v.ToArray(); - involume = bars.Volume.v.ToArray(); - } - - [Fact] - public void ADD() - { - ADD_Series QL = new(bars.Open, bars.Close); - Core.Add(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADL() - { - ADL_Series QL = new(bars); - Core.Ad(inhigh, inlow, inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > 0; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADOSC() - { - ADOSC_Series QL = new(bars, 3, 10, false); - Core.AdOsc(inhigh, inlow, inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip*2; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ATR() - { - ATR_Series QL = new(bars, period:period, useNaN: false); - Core.Atr(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - public void BBANDS() - { - double[] outMiddle = new double[bars.Count]; - double[] outUpper = new double[bars.Count]; - double[] outLower = new double[bars.Count]; - BBANDS_Series QL = new(bars.Close, period: period, multiplier: 2.0, false); - Core.Bbands(inclose, 0, bars.Count - 1, outRealUpperBand: outUpper, outRealMiddleBand: outMiddle, outRealLowerBand: outLower, out int outBegIdx, out _, optInTimePeriod: period, optInNbDevUp: 2.0, optInNbDevDn: 2.0); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL.Upper[i].v; - double TA_item = outUpper[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); - QL_item = QL.Mid[i].v; - TA_item = outMiddle[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); - QL_item = QL.Lower[i].v; - TA_item = outLower[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); - } - } - [Fact] - public void CCI() - { - CCI_Series QL = new(bars, period, false); - Core.Cci(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* CMO in TA-LIB is not valid - [Fact] - public void CMO() { - CMO_Series QL = new(bars.Close, period, false); - Core.Cmo(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - */ - [Fact] - public void CORR() - { - CORR_Series QL = new(bars.Open, bars.Close, period); - Core.Correl(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DEMA() - { - DEMA_Series QL = new(bars.Close, period, false, useSMA: false); - Core.Dema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > period*10; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DIV() - { - DIV_Series QL = new(bars.Open, bars.Close); - Core.Div(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void EMA() - { - EMA_Series QL = new(bars.Close, period, false); - Core.Ema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HL2() - { - TSeries QL = bars.HL2; - Core.MedPrice(inhigh, inlow, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLC3() - { - TSeries QL = bars.HLC3; - Core.TypPrice(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLCC4() - { - TSeries QL = bars.HLCC4; - Core.WclPrice(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void KAMA() { - KAMA_Series QL = new(bars.Close, period, fast: 2, slow: 30); - Core.Kama(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outReal: TALIB, outBegIdx: out int outBegIdx, outNbElement: out _, optInTimePeriod: period); - for (int i = QL.Length - 1; i > skip * 15; i--) { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MACD() - { - double[] macdSignal = new double[bars.Count]; - double[] macdHist = new double[bars.Count]; - MACD_Series QL = new(bars.Close, slow: 26, fast: 12, signal: 9, false); - // TA-LIB runs EMA without SMA, leaving first 100 values for convergence - Core.Macd(inclose, 0, bars.Count - 1, outMacd: TALIB, outMacdSignal: macdSignal, outMacdHist: macdHist, out int outBegIdx, out _, optInFastPeriod: 12, optInSlowPeriod: 26, optInSignalPeriod: 9); - for (int i = QL.Length - 1; i > 100; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Signal[i].v; - TA_item = macdSignal[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* - [Fact] - public void MAMA() - { - MAMA_Series QL = new(bars.Close, fastlimit: 0.5, slowlimit: 0.05); - Core.Mama(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outMama: TALIB, outFama: TALIB2, outBegIdx: out int outBegIdx, outNbElement: out _, optInFastLimit: 0.5, optInSlowLimit: 0.05); - for (int i = QL.Length - 1; i > skip * 10; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits-1), Math.Exp(-digits-1)); - } - } - */ - [Fact] - public void MAX() - { - MAX_Series QL = new(bars.Close, period, false); - Core.Max(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIDPOINT() - { - MIDPOINT_Series QL = new(bars.Close, period, false); - Core.MidPoint(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIDPRICE() - { - MIDPRICE_Series QL = new(bars, period, false); - Core.MidPrice(inhigh, inlow, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIN() - { - MIN_Series QL = new(bars.Close, period, false); - Core.Min(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MUL() - { - MUL_Series QL = new(bars.Open, bars.Close); - Core.Mult(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OBV() - { - OBV_Series QL = new(bars, period, false); - Core.Obv(inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OHLC4() - { - TSeries QL = bars.OHLC4; - Core.AvgPrice(inopen, inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void RSI() - { - RSI_Series QL = new(bars.Close, period, false); - Core.Rsi(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SDEV() - { - SDEV_Series QL = new(bars.Close, period, false); - Core.StdDev(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SMA() - { - SMA_Series QL = new(bars.Close, period, false); - Core.Sma(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUB() - { - SUB_Series QL = new(bars.Open, bars.Close); - Core.Sub(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUM() - { - CUSUM_Series QL = new(bars.Close, period, false); - Core.Sum(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void T3() - { - T3_Series QL = new(source: bars.Close, period: period, vfactor: 0.7, useNaN: false); - Core.T3(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outReal: TALIB, outBegIdx: out int outBegIdx, outNbElement: out _, optInTimePeriod: period, optInVFactor: 0.7); - for (int i = QL.Length - 1; i > period*10; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TEMA() - { - TEMA_Series QL = new(bars.Close, period, false); - Core.Tema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip * 15; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TR() - { - TR_Series QL = new(bars); - Core.TRange(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TRIMA() - { - TRIMA_Series QL = new(bars.Close, period, false); - Core.Trima(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TRIX() { - TRIX_Series QL = new(bars.Close, period, useNaN: false, useSMA: true); - Core.Trix(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > period*10; i--) { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void VAR() - { - VAR_Series QL = new(bars.Close, period, false); - Core.Var(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip * 15; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void WMA() - { - WMA_Series QL = new(bars.Close, period, false); - Core.Wma(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TA_item = TALIB[i - outBegIdx]; - Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - -} diff --git a/Tests/Validations/Trends/Tulip.cs b/Tests/Validations/Trends/Tulip.cs deleted file mode 100644 index 150f3688..00000000 --- a/Tests/Validations/Trends/Tulip.cs +++ /dev/null @@ -1,524 +0,0 @@ -using Xunit; -using System; -using Tulip; -using QuanTAlib; - -namespace Validations; -public class Tulip_Test -{ - private readonly GBM_Feed bars; - private readonly Random rnd = new(); - private readonly int period, digits, skip; - private readonly double[] outdata; - private readonly double[] inopen; - private readonly double[] inhigh; - private readonly double[] inlow; - private readonly double[] inclose; - private readonly double[] involume; - - public Tulip_Test() - { - bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3); - period = rnd.Next(28) + 3; - skip = period+5; - digits = 8; - - outdata = new double[bars.Count]; - inopen = bars.Open.v.ToArray(); - inhigh = bars.High.v.ToArray(); - inlow = bars.Low.v.ToArray(); - inclose = bars.Close.v.ToArray()!; - involume = bars.Volume.v.ToArray()!; - - } - [Fact] - public void ADL() - { - double[][] arrin = {inhigh, inlow, inclose, involume }; - double[][] arrout = { outdata }; - ADL_Series QL = new(bars); - Tulip.Indicators.ad.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADD() - { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - ADD_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.add.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ADOSC() - { - double[][] arrin = { inhigh, inlow, inclose, involume }; - double[][] arrout = { outdata }; - int s = 3; - ADOSC_Series QL = new(bars, s, period, false); - Tulip.Indicators.adosc.Run(inputs: arrin, options: new double[] { s, period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i-period+1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ATR() - { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - ATR_Series QL = new(bars, period:period, useNaN:false); - Tulip.Indicators.atr.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - //Tulip ATR doesn't use warm-up SMA, compensating with 200 warming bars - for (int i = QL.Length - 1; i > 200+skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void BBANDS() - { - double[][] arrin = { inclose }; - double[] outmid = new double[bars.Count]; - double[] outlower = new double[bars.Count]; - double[] outupper = new double[bars.Count]; - double[][] arrout = { outlower, outmid, outupper}; - BBANDS_Series QL = new(bars.Close, period, 2, false); - Tulip.Indicators.bbands.Run(inputs: arrin, options: new double[] { period, 2 }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL.Lower[i].v; - double TU_item = outlower[i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Mid[i].v; - TU_item = outmid[i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - QL_item = QL.Upper[i].v; - TU_item = outupper[i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* - [Fact] - public void CCI() { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - CCI_Series QL = new(bars, period, useNaN: false); - Tulip.Indicators.cci.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = outdata[i - period + 1]; - Assert.Equal(QL_item,TU_item); - //Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - */ - [Fact] - public void CMO() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - CMO_Series QL = new(bars.Close, period, useNaN: false); - Tulip.Indicators.cmo.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DECAY() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - DECAY_Series QL = new(bars.Close, period, useNaN: false); - Tulip.Indicators.decay.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 200; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DEMA() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - DEMA_Series QL = new(bars.Close, period, useNaN: false, useSMA: false); - Tulip.Indicators.dema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip+200; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i-(period+period-2)]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void DIV() { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - DIV_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.div.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void EDECAY() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - DECAY_Series QL = new(bars.Close, period, exponential: true, useNaN: false); - Tulip.Indicators.edecay.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip + 200; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void EMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - // Tulip EMA doesn't use SMA to warm-up - EMA_Series QL = new(bars.Close, period, false, useSMA: false); - Tulip.Indicators.ema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HL2() { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - - TSeries QL = bars.HL2; - Tulip.Indicators.medprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLC3() { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - TSeries QL = bars.HLC3; - Tulip.Indicators.typprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void HLCC4() { - double[][] arrin = { inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - TSeries QL = bars.HLCC4; - Tulip.Indicators.wcprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - public void HMA() { - int p = 10; - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - HMA_Series QL = new(bars.Close, p, false); - Tulip.Indicators.hma.Run(inputs: arrin, options: new double[] { p }, outputs: arrout); - for (int i = QL.Length - 1; i > skip+2; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - p - 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits-2), Math.Exp(-digits-2)); - } - } - - [Fact] - public void KAMA() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - KAMA_Series QL = new(bars.Close, period); - Tulip.Indicators.kama.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > 250; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - - [Fact] - public void LINREG() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - SLOPE_Series QL = new(bars.Close, period); - Tulip.Indicators.linregslope.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period+1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MACD() { - - double[] outsignal = new double[bars.Count]; - double[] outhist = new double[bars.Count]; - double[][] arrin = { inclose }; - double[][] arrout = { outdata, outsignal, outhist }; - MACD_Series QL = new(bars.Close, slow: 26,fast: 10, signal: 9); - Tulip.Indicators.macd.Run(inputs: arrin, options: new double[] { 10,26,9 }, outputs: arrout); - for (int i = QL.Length - 1; i > 150; i--) { - double QL_item = QL[i].v; - double TU_item =outdata[i - 26+1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MAX() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - MAX_Series QL = new(bars.Close, period, false); - Tulip.Indicators.max.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i-period+1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MIN() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - MIN_Series QL = new(bars.Close, period, false); - Tulip.Indicators.min.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void MUL() { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - MUL_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.mul.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OBV() { - double[][] arrin = { inclose, involume }; - double[][] arrout = { outdata }; - OBV_Series QL = new(bars, period, false); - Tulip.Indicators.obv.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i] + arrin[1][0]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void OHLC4() - { - double[][] arrin = { inopen, inhigh, inlow, inclose }; - double[][] arrout = { outdata }; - - TSeries QL = bars.OHLC4; - Tulip.Indicators.avgprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void RMA() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - RMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.wilders.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void RSI() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - RSI_Series QL = new(bars.Close, period, false); - Tulip.Indicators.rsi.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SMA() - { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - SMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.sma.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) - { - double QL_item = QL[i].v; - double TU_item = arrout[0][i-period+1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SDEV() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - SDEV_Series QL = new(bars.Close, period, false); - Tulip.Indicators.stddev.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUB() { - double[][] arrin = { inhigh, inlow }; - double[][] arrout = { outdata }; - SUB_Series QL = new(bars.High, bars.Low); - Tulip.Indicators.sub.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void SUM() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - CUSUM_Series QL = new(bars.Close, period, false); - Tulip.Indicators.sum.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TR() { - double[][] arrin = { inhigh,inlow,inclose }; - double[][] arrout = { outdata }; - TR_Series QL = new(bars); - Tulip.Indicators.tr.Run(inputs: arrin, options: new double[] {}, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TEMA() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - TEMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.tema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip+200; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - (period-1)*3]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void TRIMA() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - TRIMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.trima.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - /* - [Fact] - public void TRIX() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - TRIX_Series QL = new(bars.Close, period); - Tulip.Indicators.trix.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > period+200; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - (period*3) + 2]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits+2), Math.Exp(-digits+2)); - } - } - */ - [Fact] - public void VAR() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - VAR_Series QL = new(bars.Close, period, false); - Tulip.Indicators.var.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void WMA() { - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - WMA_Series QL = new(bars.Close, period, false); - Tulip.Indicators.wma.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); - for (int i = QL.Length - 1; i > skip; i--) { - double QL_item = QL[i].v; - double TU_item = arrout[0][i - period + 1]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); - } - } - [Fact] - public void ZLEMA() { - int p = 4; - double[][] arrin = { inclose }; - double[][] arrout = { outdata }; - ZLEMA_Series QL = new(bars.Close, p, false); - Tulip.Indicators.zlema.Run(inputs: arrin, options: new double[] { p }, outputs: arrout); - for (int i = QL.Length - 1; i > skip+20; i--) { - double QL_item = QL[i].v; - double TU_item = outdata[i]; - Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits-2), Math.Exp(-digits-2)); - } - } -} diff --git a/Tests/test_Trady.cs b/Tests/test_Trady.cs new file mode 100644 index 00000000..95e9e9b1 --- /dev/null +++ b/Tests/test_Trady.cs @@ -0,0 +1,97 @@ +using Xunit; +using Trady.Analysis.Indicator; +using Trady.Core; +using Trady.Core.Infrastructure; +using QuanTAlib; + +public class TradyTests +{ + private readonly TBarSeries bars; + private readonly GbmFeed feed; + private Random rnd; + private readonly double range; + private int period, iterations; + private int skip; + private IEnumerable Candles; + + public TradyTests() + { + rnd = new((int)DateTime.Now.Ticks); + feed = new(sigma: 0.5, mu: 0.0); + bars = new(feed); + range = 1e-9; + feed.Add(10000); + iterations = 3; + skip = 500; + Candles = bars.Select(bar => new Candle( + bar.Time, + (decimal)bar.Open, + (decimal)bar.High, + (decimal)bar.Low, + (decimal)bar.Close, + (decimal)bar.Volume + )).ToList(); + } + + [Fact] + public void SMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Sma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + + var Trady = new SimpleMovingAverage(Candles, period) + .Compute() + .Select(result => new + { + Date = result.DateTime, + Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN + }) + .ToList(); + + Assert.Equal(QL.Length, Trady.Count); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].Value; + double Tr_item = Trady[i].Value; + Assert.InRange(Tr_item - QL_item, -range, range); + } + } + } + + [Fact] + public void EMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Ema ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + + var Trady = new ExponentialMovingAverage(Candles, period) + .Compute() + .Select(result => new + { + Date = result.DateTime, + Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN + }) + .ToList(); + + Assert.Equal(QL.Length, Trady.Count); + for (int i = QL.Length - 1; i > skip*2; i--) + { + double QL_item = QL[i].Value; + double Tr_item = Trady[i].Value; + Assert.InRange(Tr_item - QL_item, -range, range); + } + } + } + + +} \ No newline at end of file diff --git a/Tests/test_Tulip.cs b/Tests/test_Tulip.cs new file mode 100644 index 00000000..5b7cb558 --- /dev/null +++ b/Tests/test_Tulip.cs @@ -0,0 +1,82 @@ +using Xunit; +using Tulip; +using QuanTAlib; + +public class TulipTests +{ + private readonly TBarSeries bars; + private readonly GbmFeed feed; + private Random rnd; + private readonly double range; + private int period, iterations; + private readonly double[] data; + private readonly double[] outdata; + private int skip; + + public TulipTests() + { + rnd = new((int)DateTime.Now.Ticks); + feed = new(sigma: 0.5, mu: 0.0); + bars = new(feed); + range = 1e-9; + feed.Add(10000); + iterations = 3; + skip = 500; + data = feed.Close.v.ToArray(); + outdata = new double[data.Count()]; + } + + [Fact] + public void SMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Sma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + + double[][] arrin = [data]; + double[][] arrout = [outdata]; + Tulip.Indicators.sma.Run(inputs: arrin, options: [period], outputs: arrout); + Assert.Equal(QL.Length, arrout[0].Length); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].Value; + double TU = i skip*2; i--) //Initial Tulip Ema value is (wrongly) set to the first input value - therefore large skip + { + double QL_item = QL[i].Value; + double TU = arrout[0][i]; + //Assert.InRange(TU - QL_item, -range, range); + Assert.True(Math.Abs(TU - QL_item) <= range, $"Assertion failed at index {i} for period {period}: TU = {TU}, QL_item = {QL_item}, delta = {TU-QL_item}"); + + } + } + } + + +} \ No newline at end of file diff --git a/Tests/test_consistency.cs b/Tests/test_consistency.cs new file mode 100644 index 00000000..72d482cd --- /dev/null +++ b/Tests/test_consistency.cs @@ -0,0 +1,883 @@ +using Xunit; +using QuanTAlib; + +public class Consistency +{ + Random rnd; + int series_len = 1000; + int corrections = 100; + + public Consistency() + { //constructor + rnd = new((int)DateTime.Now.Ticks); + } + + + [Fact] + public void CanUpdate() + { + + GbmFeed gbm = new(); + TSeries input = new(gbm.Close); + TSeries output = new(input); + + gbm.Add(10000); + + Assert.Equal(input.Count, output.Count); + for (int i = 0; i < input.Count; i++) + { + Assert.Equal(input[i].v, output[i].v); + } + } + + [Fact] + public void Alma_isNew() + { + int p = (int)rnd.Next(2, 100); + double offset = rnd.Next(); + double sigma = rnd.Next(1, 100); + Alma ma1 = new(period: p, offset: offset, sigma: sigma); + Alma ma2 = new(period: p, offset: offset, sigma: sigma); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Convolution_isNew() + { + Convolution ma1 = new(new double[] { 1.0, 2, 3, 2, 1 }); + Convolution ma2 = new(new double[] { 1.0, 2, 3, 2, 1 }); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Dema_isNew() + { + int p = (int)rnd.Next(2, 100); + Dema ma1 = new(p); + Dema ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Dsma_isNew() + { + int p = (int)rnd.Next(2, 100); + Dsma ma1 = new(p); + Dsma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Dwma_isNew() + { + int p = (int)rnd.Next(2, 100); + Dwma ma1 = new(p); + Dwma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void EmaSma_isNew() + { + int p = (int)rnd.Next(2, 100); + Ema ma1 = new(p, useSma: true); + Ema ma2 = new(p, useSma: true); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Ema_isNew() + { + int p = (int)rnd.Next(2, 100); + Ema ma1 = new(p, useSma: false); + Ema ma2 = new(p, useSma: false); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Sma_isNew() + { + int p = (int)rnd.Next(2, 100); + Sma ma1 = new(p); + Sma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Epma_isNew() + { + int p = (int)rnd.Next(2, 100); + Epma ma1 = new(p); + Epma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Frama_isNew() + { + int p = (int)rnd.Next(2, 100); + Frama ma1 = new(p); + Frama ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Fwma_isNew() + { + int p = (int)rnd.Next(2, 100); + Fwma ma1 = new(p); + Fwma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Gma_isNew() + { + int p = (int)rnd.Next(2, 100); + Gma ma1 = new(p); + Gma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Hma_isNew() + { + int p = (int)rnd.Next(2, 100); + Hma ma1 = new(p); + Hma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + + [Fact] + public void Hwma_isNew() + { + int p = (int)rnd.Next(2, 100); + Hwma ma1 = new(p); + Hwma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + /* + [Fact] + public void Jma_isNew() + { + int p = (int)rnd.Next(2, 100); + Jma ma1 = new(p); + Jma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + + //Assert.Equal(ma1.Value, ma2.Value); + Assert.True(ma1.Value == ma2.Value, $"Assertion failed at p={p}, Value={item1.Value}. ma1.Value={ma1.Value}, ma2.Value={ma2.Value}"); + } + } + */ + + + [Fact] + public void Kama_isNew() + { + int p = (int)rnd.Next(2, 100); + Kama ma1 = new(p); + Kama ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Ltma_isNew() + { + int p = rnd.Next(0, 1); + Ltma ma1 = new(p); + Ltma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Mama_isNew() + { + int p = rnd.Next(0, 1); + Mama ma1 = new(p, p * 0.1); + Mama ma2 = new(p, p * 0.1); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + + Assert.True(ma1.Value == ma2.Value, $"Assertion failed for p={p}, i={i}. Expected {ma1.Value} but got {ma2.Value}."); + } + } + + [Fact] + public void Mgdi_isNew() + { + int p = (int)rnd.Next(2, 100); + Mgdi ma1 = new(p); + Mgdi ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Mma_isNew() + { + int p = (int)rnd.Next(2, 100); + Mma ma1 = new(p); + Mma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Qema_isNew() + { + int p = (int)rnd.Next(2, 100); + Qema ma1 = new(); + Qema ma2 = new(); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Rema_isNew() + { + int p = (int)rnd.Next(2, 100); + Rema ma1 = new(p); + Rema ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Rma_isNew() + { + int p = (int)rnd.Next(2, 100); + Rma ma1 = new(p); + Rma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Sinema_isNew() + { + int p = (int)rnd.Next(2, 100); + Sinema ma1 = new(p); + Sinema ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Smma_isNew() + { + int p = (int)rnd.Next(2, 100); + Smma ma1 = new(p); + Smma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void T3_isNew() + { + int p = (int)rnd.Next(2, 100); + T3 ma1 = new(p); + T3 ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Tema_isNew() + { + int p = (int)rnd.Next(2, 100); + Tema ma1 = new(p); + Tema ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + + [Fact] + public void Trima_isNew() + { + int p = (int)rnd.Next(2, 100); + Trima ma1 = new(p); + Trima ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + + [Fact] + public void Vidya_isNew() + { + int p = (int)rnd.Next(2, 100); + Vidya ma1 = new(p); + Vidya ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Wma_isNew() + { + int p = (int)rnd.Next(2, 100); + Wma ma1 = new(p); + Wma ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + [Fact] + public void Zlema_isNew() + { + int p = (int)rnd.Next(2, 100); + Zlema ma1 = new(p); + Zlema ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Entropy_isNew() + { + int p = (int)rnd.Next(2, 100); + Entropy ma1 = new(p); + Entropy ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Kurtosis_isNew() + { + int p = (int)rnd.Next(2, 100); + Kurtosis ma1 = new(p); + Kurtosis ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Max_isNew() + { + int p = (int)rnd.Next(2, 100); + Max ma1 = new(p, 0.01); + Max ma2 = new(p, 0.01); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Min_isNew() + { + int p = (int)rnd.Next(2, 100); + Min ma1 = new(p, 0.01); + Min ma2 = new(p, 0.01); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Med_isNew() + { + int p = (int)rnd.Next(2, 100); + Median ma1 = new(p); + Median ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Mode_isNew() + { + int p = (int)rnd.Next(2, 100); + Mode ma1 = new(p); + Mode ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Percentile_isNew() + { + int p = (int)rnd.Next(2, 100); + Percentile ma1 = new(p, 50); + Percentile ma2 = new(p, 50); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Skew_isNew() + { + int p = (int)rnd.Next(2, 100); + Skew ma1 = new(p); + Skew ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Stddev_isNew() + { + int p = (int)rnd.Next(2, 100); + Stddev ma1 = new(p); + Stddev ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Variance_isNew() + { + int p = (int)rnd.Next(2, 100); + Variance ma1 = new(p); + Variance ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + + [Fact] + public void Zscore_isNew() + { + int p = (int)rnd.Next(2, 100); + Zscore ma1 = new(p); + Zscore ma2 = new(p); + for (int i = 0; i < series_len; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + ma1.Calc(item1); + for (int j = 0; j < corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + ma1.Calc(item1); + } + ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); + Assert.Equal(ma1.Value, ma2.Value); + } + } + +} diff --git a/Tests/test_skender.stock.cs b/Tests/test_skender.stock.cs new file mode 100644 index 00000000..a0932094 --- /dev/null +++ b/Tests/test_skender.stock.cs @@ -0,0 +1,324 @@ +using Xunit; +using Skender.Stock.Indicators; +using QuanTAlib; + +public class SkenderTests +{ + private readonly TBarSeries bars; + private readonly GbmFeed feed; + private Random rnd; + private readonly double range; + private int period, iterations; + private readonly IEnumerable quotes; + + + public SkenderTests() + { + rnd = new((int)DateTime.Now.Ticks); + feed = new(sigma: 0.5, mu: 0.0); + bars = new(feed); + range = 1e-9; + feed.Add(10000); + iterations = 3; + quotes = bars.Select(q => new Quote + { + Date = q.Time, + Open = (decimal)q.Open, + High = (decimal)q.High, + Low = (decimal)q.Low, + Close = (decimal)q.Close, + Volume = (decimal)q.Volume + }); + } + + [Fact] + public void SMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Sma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetSma(lookbackPeriods: period).Select(i => i.Sma.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void SMAEMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Ema ma = new(period, useSma: true); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetEma(lookbackPeriods: period).Select(i => i.Ema.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void EMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Ema ma = new(period, useSma: false); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetEma(lookbackPeriods: period).Select(i => i.Ema.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > QL.Length - 500; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void DEMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Dema ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetDema(lookbackPeriods: period).Select(i => i.Dema.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > QL.Length - 500; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void TEMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Tema ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetTema(lookbackPeriods: period).Select(i => i.Tema.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > QL.Length - 500; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void SMAConvolution() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + double[] kernel = Enumerable.Repeat(1.0, period).ToArray(); + Convolution ma = new(kernel); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetSma(lookbackPeriods: period).Select(i => i.Sma.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void WMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Wma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetWma(lookbackPeriods: period).Select(i => i.Wma.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period + 2; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void HMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Hma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetHma(lookbackPeriods: period).Select(i => i.Hma.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period + 5; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void EPMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Epma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetEpma(lookbackPeriods: period).Select(i => i.Epma.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period + 5; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void ALMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Alma ma = new(period, offset: 0.85, sigma: 6); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetAlma(lookbackPeriods: period).Select(i => i.Alma.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void T3() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + T3 ma = new(period, vfactor: 0.7, useSma: false); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetT3(lookbackPeriods: period, volumeFactor: 0.7).Select(i => i.T3.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void SMMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Smma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetSmma(lookbackPeriods: period).Select(i => i.Smma.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void KAMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Kama ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.GetKama(erPeriods: period).Select(i => i.Kama.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void MAMA() + { + for (int run = 0; run < iterations; run++) + { + //period = rnd.Next(50) + 5; + Mama ma = new(fastLimit: 0.5, slowLimit: 0.05); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.Select(q => (q.Date, (double)q.Close)) + .GetMama(fastLimit: 0.5, slowLimit: 0.05) + .Select(i => i.Mama.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > 100; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void MGDI() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Mgdi ma = new(period: period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + var SK = quotes.Select(q => (q.Date, (double)q.Close)) + .GetDynamic(lookbackPeriods: period) + .Select(i => i.Dynamic.Null2NaN()!); + Assert.Equal(QL.Length, SK.Count()); + for (int i = QL.Length - 1; i > period + 5; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + +} \ No newline at end of file diff --git a/Tests/test_talib.cs b/Tests/test_talib.cs new file mode 100644 index 00000000..82e267c9 --- /dev/null +++ b/Tests/test_talib.cs @@ -0,0 +1,152 @@ +using Xunit; +using TALib; +using QuanTAlib; + +public class TAlibTests +{ + private readonly TBarSeries bars; + private readonly GbmFeed feed; + private Random rnd; + private readonly double range; + private int period, iterations; + private readonly double[] data; + private readonly double[] TALIB; + + + public TAlibTests() + { + rnd = new((int)DateTime.Now.Ticks); + feed = new(sigma: 0.5, mu: 0.0); + bars = new(feed); + range = 1e-9; + feed.Add(10000); + iterations = 3; + data = feed.Close.v.ToArray(); + TALIB = new double[data.Count()]; + + } + + [Fact] + public void SMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Sma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + Core.Sma(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); + Assert.Equal(QL.Length, TALIB.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; + Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void EMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Ema ma = new(period, useSma: true); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + Core.Ema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); + Assert.Equal(QL.Length, TALIB.Count()); + for (int i = QL.Length - 1; i > period; i--) + { + double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; + Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void DEMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Dema ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + Core.Dema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); + Assert.Equal(QL.Length, TALIB.Count()); + for (int i = QL.Length - 1; i > period*20; i--) + { + double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; + Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); + } + } + } + + [Fact] + public void TEMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Tema ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + Core.Tema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); + Assert.Equal(QL.Length, TALIB.Count()); + for (int i = QL.Length - 1; i > period*20; i--) + { + double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; + Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); + } + } + } + +//TODO fix WMA +/* + [Fact] + public void WMA() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Wma ma = new(period); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + Core.Wma(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); + Assert.Equal(QL.Length, TALIB.Count()); + for (int i = QL.Length - 1; i > period*3; i--) + { + double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; + Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); + } + } + } + */ + + [Fact] + public void T3() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + T3 ma = new(period, vfactor: 0.7, useSma: false); + TSeries QL = new(); + foreach (TBar item in feed) + { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } + Core.T3(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period, optInVFactor: 0.7); + Assert.Equal(QL.Length, TALIB.Count()); + for (int i = QL.Length - 1; i > period*20; i--) + { + double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx]; + Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); + } + } + } + +} \ No newline at end of file diff --git a/archive/.editorconfig b/archive/.editorconfig new file mode 100644 index 00000000..b99bec2f --- /dev/null +++ b/archive/.editorconfig @@ -0,0 +1,16 @@ +# Top-most EditorConfig file +root = true + +[*.{cs,vb}] +# Suppress S3776 (Cognitive Complexity) +dotnet_diagnostic.S3776.severity = none +# Suppress CA1416 (Platform Compatibility) +dotnet_diagnostic.CA1416.severity = none +dotnet_style_parentheses_in_control_flow_statements = always_for_clarity:suggestion +csharp_new_line_before_open_brace = none +csharp_new_line_before_else = false +csharp_new_line_before_catch = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_object_initializers = false +csharp_new_line_before_members_in_anonymous_types = false +csharp_new_line_between_query_expression_clauses = false \ No newline at end of file diff --git a/archive/.gitignore b/archive/.gitignore new file mode 100644 index 00000000..f7e3d0a2 --- /dev/null +++ b/archive/.gitignore @@ -0,0 +1,358 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates +.vscode/ +.fleet/ +*.deps.json +.Sandbox/ +#.sonarlint/ +.DS_Store + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ +dotCover.Output.dcvr +/Tests/GlobalSuppressions.cs diff --git a/archive/.refactoring/base.cs b/archive/.refactoring/base.cs new file mode 100644 index 00000000..530d38d2 --- /dev/null +++ b/archive/.refactoring/base.cs @@ -0,0 +1,237 @@ +using System; + +public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) +{ + public DateTime Time { get; init; } = Time; + public double Value { get; init; } = Value; + public bool IsNew { get; init; } = IsNew; + public bool IsHot { get; init; } = IsHot; + + public TValue() : this(DateTime.UtcNow, 0) { } + public TValue(double value) : this(DateTime.UtcNow, value) { } + public TValue((DateTime time, double value) tuple) : this(tuple.time, tuple.value) { } + + public static implicit operator double(TValue tv) => tv.Value; + public static implicit operator DateTime(TValue tv) => tv.Time; + public static implicit operator TValue(double value) => new TValue(DateTime.UtcNow, value); + + public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: {Value:F2}]"; +} + + +public readonly record struct TBar(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) +{ + public DateTime Time { get; init; } = Time; + public double Open { get; init; } = Open; + public double High { get; init; } = High; + public double Low { get; init; } = Low; + public double Close { get; init; } = Close; + public double Volume { get; init; } = Volume; + public bool IsNew { get; init; } = IsNew; + + public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { } + public TBar(double open, double high, double low, double close, double volume) : this(DateTime.UtcNow, open, high, low, close, volume) { } + public TBar((DateTime time, double open, double high, double low, double close, double volume) tuple) : this(tuple.time, tuple.open, tuple.high, tuple.low, tuple.close, tuple.volume) { } + + public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]"; +} + +///////////////////// +/// +///////////////////// + +public class GBM_Feed +{ + private readonly double _mu; + private readonly double _sigma; + private readonly Random _random; + private double _lastClose; + private double _lastHigh; + private double _lastLow; + + public GBM_Feed(double initialPrice, double mu, double sigma) + { + _lastClose = initialPrice; + _lastHigh = initialPrice; + _lastLow = initialPrice; + _mu = mu; + _sigma = sigma; + _random = Random.Shared; + } + + public TBar Generate(bool IsNew = true) + { + DateTime time = DateTime.UtcNow; + double dt = 1.0 / 252; // Assuming daily steps in a trading year of 252 days + double drift = (_mu - 0.5 * _sigma * _sigma) * dt; + double diffusion = _sigma * Math.Sqrt(dt) * NormalRandom(); + double newClose = _lastClose * Math.Exp(drift + diffusion); + + double open = _lastClose; + double high = Math.Max(open, newClose) * (1 + _random.NextDouble() * 0.01); + double low = Math.Min(open, newClose) * (1 - _random.NextDouble() * 0.01); + double volume = 1000 + _random.NextDouble() * 1000; // Random volume between 1000 and 2000 + + if (!IsNew) + { + high = Math.Max(_lastHigh, high); + low = Math.Min(_lastLow, low); + } + else + { + _lastClose = newClose; + } + + _lastHigh = high; + _lastLow = low; + + return new TBar(time, open, high, low, newClose, volume, IsNew); + } + + private double NormalRandom() + { + // Box-Muller transform to generate standard normal random variable + double u1 = 1.0 - _random.NextDouble(); // Uniform(0,1] random doubles + double u2 = 1.0 - _random.NextDouble(); + return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + } +} + + +/// +/// //////////////// +/// + +public class EMA +{ + private double lastEma, lastEmaCandidate, k; + private int period, i; + public TValue Value { get; private set; } + public bool IsHot { get; private set; } + + public EMA(int period) { + Init(period); + } + + public void Init(int period) + { + this.period = period; + this.k = 2.0 / (period + 1); + this.lastEma = this.lastEmaCandidate = double.NaN; + this.i = 0; + } + public TValue Update(TValue input, bool IsNew = true) { + double ema; + + if (double.IsNaN(lastEma)) { lastEma = input.Value; } + + if (IsNew) { + lastEma = lastEmaCandidate; + i++; + } + + double kk = (i= period; + Value = new TValue(input.Time, ema, IsNew, IsHot); + return Value; + } +} + +///////////////// +/// + +public class SMA +{ + private CircularBuffer buffer; + private int period; + private double sum; + public TValue Value { get; private set; } + public bool IsHot { get; private set; } + + public SMA(int period) + { + Init(period); + } + + public void Init(int period) + { + this.period = period; + this.buffer = new CircularBuffer(period); + this.sum = 0; + this.IsHot = false; + this.Value = default; + } + + public TValue Update(TValue input, bool IsNew = true) + { + if (IsNew) + { + if (buffer.Count == period) { + sum -= buffer[0]; + } + buffer.Add(input); + sum += input.Value; + } else { + if (buffer.Count > 0) { + sum -= buffer[buffer.Count - 1]; + sum += input.Value; + buffer[buffer.Count - 1] = input; + } else { + buffer.Add(input); + sum += input.Value; + } + } + + double sma = buffer.Count > 0 ? sum / buffer.Count : double.NaN; + IsHot = buffer.Count >= period; + Value = new TValue(input.Time, sma, IsNew, IsHot); + return Value; + } +} + +///////////////////// +/// +///////////////////// + + +public class CircularBuffer +{ + private double[] _buffer; + private int _start; + private int _size; + + public CircularBuffer(int capacity) { + _buffer = new double[capacity]; + _start = 0; + _size = 0; + } + + public int Capacity => _buffer.Length; + public int Count => _size; + + public void Add(double item) { + if (_size < Capacity) { + _buffer[(_start + _size) % Capacity] = item; + _size++; + } else { + _buffer[_start] = item; + _start = (_start + 1) % Capacity; + } + } + + public double this[int index] { + get { + if (index < 0 || index >= _size) + throw new IndexOutOfRangeException(); + return _buffer[(_start + index) % Capacity]; + } + set { + if (index < 0 || index >= _size) + throw new IndexOutOfRangeException(); + _buffer[(_start + index) % Capacity] = value; + } + } +} \ No newline at end of file diff --git a/archive/.refactoring/test.dib b/archive/.refactoring/test.dib new file mode 100644 index 00000000..d2277e66 --- /dev/null +++ b/archive/.refactoring/test.dib @@ -0,0 +1,163 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "\bin\Debug\calculations.dll" +using QuanTAlib; + +#!csharp + +TValue vv = new(10); +display(vv.ToString()); +display(vv.IsHot); + +TBar bb = new(1,1,1,1,10); +display(bb.ToString()); +display(bb.IsNew); + +#!csharp + +int i=10; +SMA sma = new(i); +Console.WriteLine($"{"Close",10} {"SMA(" + i + ")",10}"); +for (int i = 0; i < 20; i++) +{ + TValue c =(double)i+1; + sma.Update(10000,true); + sma.Update(1,false); + sma.Update(-1000,false); + sma.Update(c,false); + + Console.WriteLine($"{i+1} {(double)c,10:F2} {(double)sma.Value,10:F2} {sma.Value.IsHot}"); +} + +#!csharp + +public class Emitter { + private Random random = new Random(); + public event EventHandler> Pub; + public void Emit() { + DateTime now = DateTime.Now; + double randomValue = random.NextDouble() * 100; // Generates a random number between 0 and 100 + TValue value = new TValue(now, randomValue); + + EventArg eventArg = new EventArg(value, true, true); + OnValuePub(eventArg); + } + protected virtual void OnValuePub(EventArg eventArg) { + Pub?.Invoke(this, eventArg); + } +} + +public class BarEmitter +{ + private Random random = new Random(); + public event EventHandler> Pub; + private double lastClose = 100.0; // Starting price + + public void Emit() + { + double open = lastClose; + double close = open * (1 + (random.NextDouble() - 0.5) * 0.02); // +/- 1% change + double high = Math.Max(open, close) * (1 + random.NextDouble() * 0.005); // Up to 0.5% higher + double low = Math.Min(open, close) * (1 - random.NextDouble() * 0.005); // Up to 0.5% lower + double volume = random.NextDouble() * 1000000; // Random volume between 0 and 1,000,000 + + TBar bar = new TBar(DateTime.Now, open, high, low, close, volume); + lastClose = close; + + EventArg eventArg = new EventArg(bar, true, true); + OnBarPub(eventArg); + } + + protected virtual void OnBarPub(EventArg eventArg) + { + Pub?.Invoke(this, eventArg); + } +} + + +public class Listener +{ + public void Sub(object sender, EventArgs e) + { + if (e is EventArg tValueArg) { + Console.WriteLine($"TValue: {tValueArg.Data.Value:F2}"); + } else if (e is EventArg tBarArg) { + Console.WriteLine($"TBar: o={tBarArg.Data.Open:F2}, v={tBarArg.Data.Volume:F2}"); + } else { + Console.WriteLine($"Unknown type: {e.GetType().Name}"); + } + } +} + +#!csharp + +Emitter em1 = new(); +BarEmitter em2 = new(); +Listener list = new(); + +em1.Pub += list.Sub; +em2.Pub += list.Sub; + +// Emit 5 random values +for (int i = 0; i < 3; i++) { + em1.Emit(); + em2.Emit(); +} + +#!csharp + +public abstract class Indicator { + protected Indicator() { + Init(); } + public virtual void Init() {} + public virtual TValue Calc(TValue input, bool isNew=true, bool isHot=true) { + return new TValue(); + } +} + +public class EMA : Indicator +{ + private double lastEma, lastEmaCandidate, k; + private int period, i; + + public EMA(int period) { + Init(period); + } + + public void Init(int period) + { + this.period = period; + this.k = 2.0 / (period + 1); + this.lastEma = this.lastEmaCandidate = double.NaN; + this.i = 0; + } + + public override TValue Calc(TValue input, bool isNew = true, bool isHot = true) { + double ema; + + if (double.IsNaN(lastEma)) { lastEma = lastEmaCandidate = input.Value; } + + if (isNew) { + lastEma = lastEmaCandidate; + i++; + } + + double kk = (i>=period)?k:(2.0/(i+1)); + ema = lastEma + kk * (input.Value - lastEma); + lastEmaCandidate = ema; + + return new TValue(input.Timestamp, ema); + } +} + +#!csharp + +EMA ema = new(3); +display(ema.Calc(100)); +display(ema.Calc(0,false)); +display(ema.Calc(100,false)); +display(ema.Calc(0)); diff --git a/.sonarlint/QuanTAlib.ruleset b/archive/.sonarlint/QuanTAlib.ruleset similarity index 100% rename from .sonarlint/QuanTAlib.ruleset rename to archive/.sonarlint/QuanTAlib.ruleset diff --git a/.sonarlint/QuanTAlib.slconfig b/archive/.sonarlint/QuanTAlib.slconfig similarity index 100% rename from .sonarlint/QuanTAlib.slconfig rename to archive/.sonarlint/QuanTAlib.slconfig diff --git a/.sonarlint/mihakralj_quantalib/CSharp/SonarLint.xml b/archive/.sonarlint/mihakralj_quantalib/CSharp/SonarLint.xml similarity index 100% rename from .sonarlint/mihakralj_quantalib/CSharp/SonarLint.xml rename to archive/.sonarlint/mihakralj_quantalib/CSharp/SonarLint.xml diff --git a/.sonarlint/mihakralj_quantalib_secrets_settings.json b/archive/.sonarlint/mihakralj_quantalib_secrets_settings.json similarity index 100% rename from .sonarlint/mihakralj_quantalib_secrets_settings.json rename to archive/.sonarlint/mihakralj_quantalib_secrets_settings.json diff --git a/.sonarlint/mihakralj_quantalibcsharp.ruleset b/archive/.sonarlint/mihakralj_quantalibcsharp.ruleset similarity index 97% rename from .sonarlint/mihakralj_quantalibcsharp.ruleset rename to archive/.sonarlint/mihakralj_quantalibcsharp.ruleset index a2e953a9..5ad478ec 100644 --- a/.sonarlint/mihakralj_quantalibcsharp.ruleset +++ b/archive/.sonarlint/mihakralj_quantalibcsharp.ruleset @@ -1,390 +1,390 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.sonarlint/sonar.settings.json b/archive/.sonarlint/sonar.settings.json similarity index 100% rename from .sonarlint/sonar.settings.json rename to archive/.sonarlint/sonar.settings.json diff --git a/archive/Calculations/Basics/ADD_Series.cs b/archive/Calculations/Basics/ADD_Series.cs new file mode 100644 index 00000000..6d0436fe --- /dev/null +++ b/archive/Calculations/Basics/ADD_Series.cs @@ -0,0 +1,32 @@ +namespace QuanTAlib; +using System; + +/* +ADD - adding TSeries+TSeries together, or TSeries+double, or double+TSeries + +Remarks: + Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator. + + */ + +public class ADD_Series : Pair_TSeries_Indicator +{ + public ADD_Series(TSeries d1, TSeries d2) : base(d1, d2) + { + if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + } + public ADD_Series(TSeries d1, double dd2) : base(d1, dd2) + { + if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } + } + public ADD_Series(double dd1, TSeries d2) : base(dd1, d2) + { + if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } + } + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, TValue1.v + TValue2.v); + if (update) { base[base.Count - 1] = result; } else { base.Add(result); } + } +} diff --git a/archive/Calculations/Basics/CORR_Series.cs b/archive/Calculations/Basics/CORR_Series.cs new file mode 100644 index 00000000..b5209361 --- /dev/null +++ b/archive/Calculations/Basics/CORR_Series.cs @@ -0,0 +1,52 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +CORR: Pearson's Correlation Coefficient + PCC is a measure of linear correlation between two sets of data. + It is the ratio between the covariance of two variables and the product of + their standard deviations; it is essentially a normalized measurement of + the covariance, such that the result always has a value between −1 and 1. + +Sources: + https://en.wikipedia.org/wiki/Pearson_correlation_coefficient + + */ + +public class CORR_Series : Pair_TSeries_Indicator +{ + public CORR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN) + { + if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + } + + private readonly System.Collections.Generic.List _x = new(); + private readonly System.Collections.Generic.List _xx = new(); + private readonly System.Collections.Generic.List _y = new(); + private readonly System.Collections.Generic.List _yy = new(); + private readonly System.Collections.Generic.List _xy = new(); + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + Add_Replace_Trim(_x, TValue1.v, _p, update); + Add_Replace_Trim(_xx, TValue1.v * TValue1.v, _p, update); + Add_Replace_Trim(_y, TValue2.v, _p, update); + Add_Replace_Trim(_yy, TValue2.v * TValue2.v, _p, update); + Add_Replace_Trim(_xy, TValue1.v * TValue2.v, _p, update); + + double _sumx = _x.Sum(); + double _sumxx = _xx.Sum(); + double _sumy = _y.Sum(); + double _sumyy = _yy.Sum(); + double _sumxy = _xy.Sum(); + + double _covar = (_sumxx - _sumx * _sumx / _p) * (_sumyy - _sumy * _sumy / _p); + double _cor = (_covar != 0) ? (_sumxy - _sumx * _sumy / _p) / Math.Sqrt(_covar) : 0.0; + + var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _cor); + if (update) { base[base.Count - 1] = result; } else { base.Add(result); } + + } +} diff --git a/archive/Calculations/Basics/COVAR_Series.cs b/archive/Calculations/Basics/COVAR_Series.cs new file mode 100644 index 00000000..ee700873 --- /dev/null +++ b/archive/Calculations/Basics/COVAR_Series.cs @@ -0,0 +1,48 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +COVAR: Covariance + Covariance is defined as the expected value (or mean) of the product + of their deviations from their individual expected values. + +Sources: + https://en.wikipedia.org/wiki/Covariance + + */ + + +public class COVAR_Series : Pair_TSeries_Indicator +{ + public COVAR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN) + { + if (base._d1.Count > 0 && base._d2.Count > 0) + { + for (int i = 0; i < base._d1.Count; i++) + { + this.Add(base._d1[i], base._d2[i], false); + } + } + } + + private readonly System.Collections.Generic.List _x = new(); + private readonly System.Collections.Generic.List _y = new(); + private readonly System.Collections.Generic.List _xy = new(); + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + BufferTrim(_x, TValue1.v, _p, update); + BufferTrim(_y, TValue2.v, _p, update); + BufferTrim(_xy, TValue1.v * TValue2.v, _p, update); + + double _avgx = _x.Average(); + double _avgy = _y.Average(); + double _avgxy = _xy.Average(); + double _covar = _avgxy - (_avgx * _avgy); + + var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _covar); + if (update) { base[base.Count - 1] = result; } else { base.Add(result); } + } +} diff --git a/archive/Calculations/Basics/DIV_Series.cs b/archive/Calculations/Basics/DIV_Series.cs new file mode 100644 index 00000000..962e4f6f --- /dev/null +++ b/archive/Calculations/Basics/DIV_Series.cs @@ -0,0 +1,32 @@ +namespace QuanTAlib; +using System; + +/* +DIV - divide TSeries/TSeries , or TSeries/double, or double/TSeries + +Remarks: + Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator. + */ + +public class DIV_Series : Pair_TSeries_Indicator +{ + public DIV_Series(TSeries d1, TSeries d2) : base(d1, d2) + { + if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + } + public DIV_Series(TSeries d1, double dd2) : base(d1, dd2) + { + if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } + } + public DIV_Series(double dd1, TSeries d2) : base(dd1, d2) + { + if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } + } + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, + (TValue2.v is not 0) ? TValue1.v / TValue2.v : Double.PositiveInfinity); + if (update) { base[base.Count - 1] = result; } else { base.Add(result); } + } +} \ No newline at end of file diff --git a/archive/Calculations/Basics/MUL_Series.cs b/archive/Calculations/Basics/MUL_Series.cs new file mode 100644 index 00000000..b2bae613 --- /dev/null +++ b/archive/Calculations/Basics/MUL_Series.cs @@ -0,0 +1,30 @@ +namespace QuanTAlib; +using System; + +/* +MUL - multiply TSeries*TSeries together, or TSeries*double, or double*TSeries + + */ + +public class MUL_Series : Pair_TSeries_Indicator +{ + public MUL_Series(TSeries d1, TSeries d2) : base(d1, d2) + { + if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + } + public MUL_Series(TSeries d1, double dd2) : base(d1, dd2) + { + if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } + } + public MUL_Series(double dd1, TSeries d2) : base(dd1, d2) + { + if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } + } + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, + TValue1.v * TValue2.v); + if (update) { base[base.Count - 1] = result; } else { base.Add(result); } + } +} \ No newline at end of file diff --git a/archive/Calculations/Basics/SUB_Series.cs b/archive/Calculations/Basics/SUB_Series.cs new file mode 100644 index 00000000..e4333ec6 --- /dev/null +++ b/archive/Calculations/Basics/SUB_Series.cs @@ -0,0 +1,31 @@ +namespace QuanTAlib; +using System; + +/* +SUB - subtracting TSeries-TSeries, or TSeries-double, or double-TSeries + + */ + + +public class SUB_Series : Pair_TSeries_Indicator +{ + public SUB_Series(TSeries d1, TSeries d2) : base(d1, d2) + { + if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + } + public SUB_Series(TSeries d1, double dd2) : base(d1, dd2) + { + if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } + } + public SUB_Series(double dd1, TSeries d2) : base(dd1, d2) + { + if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } + } + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, + TValue1.v - TValue2.v); + if (update) { base[base.Count - 1] = result; } else { base.Add(result); } + } +} \ No newline at end of file diff --git a/Calculations/Calculations.csproj b/archive/Calculations/Calculations.csproj similarity index 97% rename from Calculations/Calculations.csproj rename to archive/Calculations/Calculations.csproj index 0ed29821..701f67f7 100644 --- a/Calculations/Calculations.csproj +++ b/archive/Calculations/Calculations.csproj @@ -1,80 +1,80 @@ - - - - QuanTAlib - 0.2.30 - 0.2.30 - 0.2.30 - Library of TA Calculations, Charts and Strategies for Quantower - Quantitative Technical Analysis Library in C# for Quantower - git - https://github.com/mihakralj/QuanTAlib - true - Miha Kralj - Miha Kralj - Apache-2.0 - readme.md - net8.0;net7.0 - disable - preview - disable - true - en-US - QuanTAlib - QuanTAlib - True - AnyCPU - False - full - True - True - - Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo; - AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex; - Quantitative;Historical;Quotes; - - - - - - - - - - full - True - 7 - True - anycpu - - - full - True - 7 - True - anycpu - - - QuanTAlib2.png - https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png - True - ..\.sonarlint\mihakralj_quantalibcsharp.ruleset - 0.2.1-dev.2 - - - - - - - True - - - - - True - False - - - - + + + + QuanTAlib + 0.2.30 + 0.2.30 + 0.2.30 + Library of TA Calculations, Charts and Strategies for Quantower + Quantitative Technical Analysis Library in C# for Quantower + git + https://github.com/mihakralj/QuanTAlib + true + Miha Kralj + Miha Kralj + Apache-2.0 + readme.md + net8.0;net7.0 + disable + preview + disable + true + en-US + QuanTAlib + QuanTAlib + True + AnyCPU + False + full + True + True + + Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo; + AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex; + Quantitative;Historical;Quotes; + + + + + + + + + + full + True + 7 + True + anycpu + + + full + True + 7 + True + anycpu + + + QuanTAlib2.png + https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png + True + ..\.sonarlint\mihakralj_quantalibcsharp.ruleset + 0.2.1-dev.2 + + + + + + + True + + + + + True + False + + + + \ No newline at end of file diff --git a/archive/Calculations/ClassStructures/Pair_TSeries_Abstract.cs b/archive/Calculations/ClassStructures/Pair_TSeries_Abstract.cs new file mode 100644 index 00000000..5d1dc59f --- /dev/null +++ b/archive/Calculations/ClassStructures/Pair_TSeries_Abstract.cs @@ -0,0 +1,157 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +Abstract classes with all scaffolding required to build indicators. + All abstracts support period, NaN, and all permutations of Add() methods. + Indicator classess need to implement: + - Chaining constructor (Abstract's constructor executes first) + - Default Add(value) class + - optional Add(series) bulk insert class (for optimization of historical analysis) + + Single_TSeries_Indicator - one single-value TSeries in, one TSeries out. + Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring) + Single_TBars_Indicator - One OHLCV TBars in, one TSeries out. + + */ + +public abstract class Pair_TSeries_Indicator : TSeries +{ + protected readonly int _p; + protected readonly bool _NaN; + protected readonly TSeries _d1; + protected readonly TSeries _d2; + protected readonly double _dd1, _dd2; + + // Chainable Constructors - add them at the end of primary constructors if needed + protected Pair_TSeries_Indicator(TSeries source1, TSeries source2, int period, bool useNaN) + { + _p = period; + _NaN = useNaN; + _d1 = source1; + _d2 = source2; + _dd1 = double.NaN; + _dd2 = double.NaN; + _d1.Pub += Sub; + _d2.Pub += Sub; + } + + protected Pair_TSeries_Indicator(TSeries source1, TSeries source2) + { + _d1 = source1; + _d2 = source2; + _dd1 = double.NaN; + _dd2 = double.NaN; + _d1.Pub += Sub; + _d2.Pub += Sub; + } + + protected Pair_TSeries_Indicator(TSeries source1, double dd2) + { + _d1 = source1; + _d2 = new TSeries(); + _dd1 = double.NaN; + _dd2 = dd2; + _d1.Pub += Sub; + } + + protected Pair_TSeries_Indicator(double dd1, TSeries source2) + { + _d1 = new TSeries(); + _d2 = source2; + _dd1 = dd1; + _dd2 = double.NaN; + _d2.Pub += Sub; + } + + // overridable Add(Tvalue, Tvalue) method to add/update a single value at the end of the list + public virtual void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2, bool update) + { + base.Add((TValue1.t, 0), update); + // default inserts zeros + } + + // potentially overridable Add() bulk variations (could be replaced with faster bulk algos) + public virtual void Add(TSeries d1, TSeries d2) + { + for (var i = 0; i < d1.Count; i++) + { + Add(d1[i], d2[i], false); + } + } + + public virtual void Add(TSeries d1, double dd2) + { + for (var i = 0; i < d1.Count; i++) + { + Add(d1[i], (d1[i].t, dd2), false); + } + } + + public virtual void Add(double dd1, TSeries d2) + { + for (var i = 0; i < d2.Count; i++) + { + Add((d2[i].t, dd1), d2[i], false); + } + } + + public void Add((DateTime t, double v) TValue1, (DateTime t, double v) TValue2) + { + Add(TValue1, TValue2, false); + } + + public void Add(bool update) + { + if (_dd1 is double.NaN && _dd2 is double.NaN) + { + // (Series, Series) + if (update || (_d1.Count > Count && _d2.Count > Count)) + { + Add(_d1[_d1.Count - 1], _d2[_d2.Count - 1], update); + } + } + else if (_dd2 is not double.NaN && _dd1 is double.NaN) + { + // (Series, Double) + Add(_d1[_d1.Count - 1], (_d1[_d1.Count - 1].t, _dd2), update); + } + else + { + // (Double, Series) + Add((_d2[_d2.Count - 1].t, _dd1), _d2[_d2.Count - 1], update); + } + } + + public void Add() + { + Add(false); + } + + public new void Sub(object source, TSeriesEventArgs e) + { + Add(e.update); + } + + protected static void Add_Replace(List l, double v, bool update) + { + if (update) + { + l[l.Count - 1] = v; + } + else + { + l.Add(v); + } + } + + protected static void Add_Replace_Trim(List l, double v, int p, bool update) + { + Add_Replace(l, v, update); + if (l.Count > p && p != 0) + { + l.RemoveAt(0); + } + } +} diff --git a/Calculations/Feeds/Alphavantage_Feed.cs b/archive/Calculations/Feeds/Alphavantage_Feed.cs similarity index 94% rename from Calculations/Feeds/Alphavantage_Feed.cs rename to archive/Calculations/Feeds/Alphavantage_Feed.cs index 5c9fc35f..f2bcc60e 100644 --- a/Calculations/Feeds/Alphavantage_Feed.cs +++ b/archive/Calculations/Feeds/Alphavantage_Feed.cs @@ -13,7 +13,7 @@ Alphavantage - Free API to collect 100 recent daily quotes. It requires a (free) */ public class Alphavantage_Feed : TBars { - public enum Interval { Month, Week, Day, Hour, Min30, Min15, Min5, Min1} + public enum Interval { Month, Week, Day, Hour, Min30, Min15, Min5, Min1 } public Alphavantage_Feed(string Symbol = "IBM", string APIkey = "demo") { System.Net.Http.HttpClient client = new(); @@ -22,8 +22,8 @@ public class Alphavantage_Feed : TBars var msg = client.GetStringAsync(req).Result; var jres = JsonSerializer.Deserialize(msg).RootElement; jres.TryGetProperty("Time Series (Daily)", out JsonElement json); - - if (json.ValueKind == JsonValueKind.Undefined) {throw new InvalidOperationException("Stock symbol "+Symbol+" not found"); } + + if (json.ValueKind == JsonValueKind.Undefined) { throw new InvalidOperationException("Stock symbol " + Symbol + " not found"); } foreach (var val in json.EnumerateObject()) { base.Add(GetOHLC(val)); } base.Reverse(); } diff --git a/Calculations/Feeds/GBM_Feed.cs b/archive/Calculations/Feeds/GBM_Feed.cs similarity index 57% rename from Calculations/Feeds/GBM_Feed.cs rename to archive/Calculations/Feeds/GBM_Feed.cs index a08568c1..c99a282f 100644 --- a/Calculations/Feeds/GBM_Feed.cs +++ b/archive/Calculations/Feeds/GBM_Feed.cs @@ -23,41 +23,45 @@ public class GBM_Feed : TBars private double seed; readonly double drift, volatility; readonly int precision; - public GBM_Feed(int Bars = 252, double Volatility = 1.0, double Drift = 0.05, double Seed = 100.0, int Precision = 2) { + public GBM_Feed(int Bars = 252, double Volatility = 1.0, double Drift = 0.05, double Seed = 100.0, int Precision = 2) + { this.seed = Seed; - volatility = Volatility*0.01; - drift = Drift*0.01; + volatility = Volatility * 0.01; + drift = Drift * 0.01; precision = Precision; - for (int i = 0; i OCMin)? (2 * OCMin) - Low : Low; + double OCMin = Math.Min(Open, Close); + double Low = (GBM_value(seed, volatility * 0.5, 0, precision)); + Low = (Low > OCMin) ? (2 * OCMin) - Low : Low; - double Volume = GBM_value(seed*10, volatility*2, Drift:0, precision: 1); + double Volume = GBM_value(seed * 10, volatility * 2, Drift: 0, precision: 1); base.Add((timestamp, Open, High, Low, Close, Volume), update); seed = Close; } - private static double GBM_value(double Seed, double Volatility, double Drift, int precision) { + private static double GBM_value(double Seed, double Volatility, double Drift, int precision) + { Random rnd = new(); - double U1 = 1.0-rnd.NextDouble(); - double U2 = 1.0-rnd.NextDouble(); + double U1 = 1.0 - rnd.NextDouble(); + double U2 = 1.0 - rnd.NextDouble(); double Z = Math.Sqrt(-2.0 * Math.Log(U1)) * Math.Sin(2.0 * Math.PI * U2); - return Math.Round(Seed * Math.Exp( Drift - (Volatility*Volatility*0.5) + (Volatility * Z)), digits: precision); + return Math.Round(Seed * Math.Exp(Drift - (Volatility * Volatility * 0.5) + (Volatility * Z)), digits: precision); } } \ No newline at end of file diff --git a/Calculations/Feeds/RND_Feed.cs b/archive/Calculations/Feeds/RND_Feed.cs similarity index 100% rename from Calculations/Feeds/RND_Feed.cs rename to archive/Calculations/Feeds/RND_Feed.cs diff --git a/Calculations/Feeds/Yahoo_Feed.cs b/archive/Calculations/Feeds/Yahoo_Feed.cs similarity index 56% rename from Calculations/Feeds/Yahoo_Feed.cs rename to archive/Calculations/Feeds/Yahoo_Feed.cs index 5b9029a1..1d87e97d 100644 --- a/Calculations/Feeds/Yahoo_Feed.cs +++ b/archive/Calculations/Feeds/Yahoo_Feed.cs @@ -14,34 +14,36 @@ Yahoo Finance - Free API feed to collect daily market quotes */ public class Yahoo_Feed : TBars { - public Yahoo_Feed(string Symbol = "IBM", int Period = 252) { - Period = (int)(Period*1.45); - string requestUrl = "https://query1.finance.yahoo.com/v8/finance/chart/"+ - Symbol+"?interval=1d&period1="+ - (int)new DateTimeOffset(DateTime.UtcNow.AddDays(-Period+1)).ToUnixTimeSeconds()+"&period2="+ + public Yahoo_Feed(string Symbol = "IBM", int Period = 252) + { + Period = (int)(Period * 1.45); + string requestUrl = "https://query1.finance.yahoo.com/v8/finance/chart/" + + Symbol + "?interval=1d&period1=" + + (int)new DateTimeOffset(DateTime.UtcNow.AddDays(-Period + 1)).ToUnixTimeSeconds() + "&period2=" + (int)new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); System.Net.Http.HttpClient client = new(); var msg = client.GetStringAsync(requestUrl).Result; var jresult = JsonSerializer.Deserialize(msg).RootElement; - jresult.TryGetProperty("chart",out JsonElement json); - json.TryGetProperty("result",out json); - json[0].TryGetProperty("timestamp",out JsonElement datetime); - json[0].TryGetProperty("indicators",out json); - json.TryGetProperty("quote",out json); - json[0].TryGetProperty("open",out JsonElement open); - json[0].TryGetProperty("high",out JsonElement high); - json[0].TryGetProperty("low",out JsonElement low); - json[0].TryGetProperty("close",out JsonElement close); - json[0].TryGetProperty("volume",out JsonElement volume); + jresult.TryGetProperty("chart", out JsonElement json); + json.TryGetProperty("result", out json); + json[0].TryGetProperty("timestamp", out JsonElement datetime); + json[0].TryGetProperty("indicators", out json); + json.TryGetProperty("quote", out json); + json[0].TryGetProperty("open", out JsonElement open); + json[0].TryGetProperty("high", out JsonElement high); + json[0].TryGetProperty("low", out JsonElement low); + json[0].TryGetProperty("close", out JsonElement close); + json[0].TryGetProperty("volume", out JsonElement volume); - for (int i=0; i +COMPARE - Generates +1 if A is above B, -1 if A is below B and 0 if A=B + + + */ + +public class COMPARE_Series : Pair_TSeries_Indicator +{ + + public COMPARE_Series(TSeries d1, TSeries d2) : base(d1, d2) + { + if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + } + public COMPARE_Series(TSeries d1, double dd2) : base(d1, dd2) + { + if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } + } + public COMPARE_Series(double dd1, TSeries d2) : base(dd1, d2) + { + if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } + } + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + + double val = TValue1.v > TValue2.v ? 1 : -1; + val = TValue1.v == TValue2.v ? 0 : val; + (System.DateTime t, double v) over = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, TValue1.v > TValue2.v ? 1 : val); + if (update) { base[^1] = over; } + else { base.Add(over); } + + + } +} + + diff --git a/archive/Calculations/Logic/CROSS_Series.cs b/archive/Calculations/Logic/CROSS_Series.cs new file mode 100644 index 00000000..b9a40d9b --- /dev/null +++ b/archive/Calculations/Logic/CROSS_Series.cs @@ -0,0 +1,49 @@ +namespace QuanTAlib; +using System; + +/* +OVER - Generates +1 if A is above B, -1 if A is below B and 0 if A=B + +Remarks: + OVER.Cross generates 1 when A breaks B from below and -1 when A breaks B from above + + */ + +public class CROSS_Series : Pair_TSeries_Indicator +{ + public TSeries Cross { get; set; } = new(); + + private double _previous = double.NaN; + public CROSS_Series(TSeries d1, TSeries d2) : base(d1, d2) + { + if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } } + } + public CROSS_Series(TSeries d1, double dd2) : base(d1, dd2) + { + if (base._d1.Count > 0) { for (int i = 0; i < base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } } + } + public CROSS_Series(double dd1, TSeries d2) : base(dd1, d2) + { + if (base._d2.Count > 0) { for (int i = 0; i < base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } } + } + + public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update) + { + + double val = TValue1.v > TValue2.v ? 1 : -1; + val = TValue1.v == TValue2.v ? 0 : val; + double over = TValue1.v > TValue2.v ? 1 : val; + + val = (_previous < over) ? 1 : -1; + val = ((_previous == over) || Double.IsNaN(this._previous) || (this._previous == 0)) ? 0 : val; + (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, val); + + this._previous = over; + + if (update) { base[^1] = result; } + else { base.Add(result); } + + } +} + + diff --git a/Calculations/Logic/EQUITY_Series.cs b/archive/Calculations/Logic/EQUITY_Series.cs similarity index 96% rename from Calculations/Logic/EQUITY_Series.cs rename to archive/Calculations/Logic/EQUITY_Series.cs index 36a97667..8323feb7 100644 --- a/Calculations/Logic/EQUITY_Series.cs +++ b/archive/Calculations/Logic/EQUITY_Series.cs @@ -1,91 +1,91 @@ -namespace QuanTAlib; -using System; - -/* -EQUITY - Generates P&L portfolio based on trades signals and equity prices - - */ - - -//base prices: bars.close -//trade signals: trades -//optional: long, short, long&short -//optional: warmup period: warmup - -/* - -public class EQUITY_Series : Single_TSeries_Indicator { - readonly TSeries inmarket; //for every bar - private readonly TSeries _price; - private double _equity; - private readonly double _capital; - - readonly int _warmup; - double _cash; - int _units; - private bool _longbuy, _longsell; - double _long_order, _open_order; - double _investment_value; - short _inmarket; - - public EQUITY_Series(TSeries signal, TSeries price, int warmup = 0, double capital = 1000) : base(signal, period: 0, useNaN: false) { - _capital = capital; - _cash = _capital; - _investment_value = 0; - _warmup = (warmup > 0) ? warmup : 1; - - inmarket = new(); - _longbuy = _longsell = false; - _open_order = 0; - _inmarket = 0; - _units = 0; - _long_order = 0; - - _price = price; //we buy on the Open price of the NEXT bar - _long_order = 0; - - if (base._data.Count > 0) { base.Add(base._data); } - } - - public override void Add((System.DateTime t, double v) TValue, bool update) { - - if (this.Count > _warmup) { - - // harvest the gain-loss from previous day - _investment_value = _units * _price[this.Count - 1].v; - _equity = _cash + _investment_value; - - - //execute orders from previous bar - if (_longbuy && _inmarket == 0) { //time to execute the long buy - _units = (int)(_cash / _price[this.Count - 1].v); - _long_order = _units * _price[this.Count - 1].v; - _cash -= _long_order; - _open_order = _long_order; - _equity = _cash + _open_order; - _inmarket = 1; - _longbuy = false; - } - - if (_longsell && _inmarket == 1) { //time to execute the long sell - _long_order = (_units * _price[this.Count - 1].v); - _cash += _long_order; - _units = 0; - - _open_order = 0; - _equity = _cash + _open_order; - _inmarket = 0; - _longsell = false; - } - - if (_inmarket == 0 && TValue.v == 1) { _longbuy = true; } //out of market, enter long - if (_inmarket == 1 && TValue.v == -1) { _longsell = true; } //long market, exit long - - //Console.WriteLine($"{TValue.v,3}\t {(_inmarket)} : {_cash,10:f2} + {_units*_price[^1].v,7:f2} = {_equity-_capital:f2}"); - } - inmarket.Add((TValue.t, (double)_inmarket)); - base.Add((TValue.t, _equity), update, _NaN); - } -} - +namespace QuanTAlib; +using System; + +/* +EQUITY - Generates P&L portfolio based on trades signals and equity prices + + */ + + +//base prices: bars.close +//trade signals: trades +//optional: long, short, long&short +//optional: warmup period: warmup + +/* + +public class EQUITY_Series : Single_TSeries_Indicator { + readonly TSeries inmarket; //for every bar + private readonly TSeries _price; + private double _equity; + private readonly double _capital; + + readonly int _warmup; + double _cash; + int _units; + private bool _longbuy, _longsell; + double _long_order, _open_order; + double _investment_value; + short _inmarket; + + public EQUITY_Series(TSeries signal, TSeries price, int warmup = 0, double capital = 1000) : base(signal, period: 0, useNaN: false) { + _capital = capital; + _cash = _capital; + _investment_value = 0; + _warmup = (warmup > 0) ? warmup : 1; + + inmarket = new(); + _longbuy = _longsell = false; + _open_order = 0; + _inmarket = 0; + _units = 0; + _long_order = 0; + + _price = price; //we buy on the Open price of the NEXT bar + _long_order = 0; + + if (base._data.Count > 0) { base.Add(base._data); } + } + + public override void Add((System.DateTime t, double v) TValue, bool update) { + + if (this.Count > _warmup) { + + // harvest the gain-loss from previous day + _investment_value = _units * _price[this.Count - 1].v; + _equity = _cash + _investment_value; + + + //execute orders from previous bar + if (_longbuy && _inmarket == 0) { //time to execute the long buy + _units = (int)(_cash / _price[this.Count - 1].v); + _long_order = _units * _price[this.Count - 1].v; + _cash -= _long_order; + _open_order = _long_order; + _equity = _cash + _open_order; + _inmarket = 1; + _longbuy = false; + } + + if (_longsell && _inmarket == 1) { //time to execute the long sell + _long_order = (_units * _price[this.Count - 1].v); + _cash += _long_order; + _units = 0; + + _open_order = 0; + _equity = _cash + _open_order; + _inmarket = 0; + _longsell = false; + } + + if (_inmarket == 0 && TValue.v == 1) { _longbuy = true; } //out of market, enter long + if (_inmarket == 1 && TValue.v == -1) { _longsell = true; } //long market, exit long + + //Console.WriteLine($"{TValue.v,3}\t {(_inmarket)} : {_cash,10:f2} + {_units*_price[^1].v,7:f2} = {_equity-_capital:f2}"); + } + inmarket.Add((TValue.t, (double)_inmarket)); + base.Add((TValue.t, _equity), update, _NaN); + } +} + */ \ No newline at end of file diff --git a/archive/Calculations/Logic/TOrders.cs b/archive/Calculations/Logic/TOrders.cs new file mode 100644 index 00000000..ec681ec5 --- /dev/null +++ b/archive/Calculations/Logic/TOrders.cs @@ -0,0 +1,38 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data; +using System.Linq; + + +public enum OType +{ + NIL = 0, // No position + BTO = 1, // Buy to Open + STC = 2, // Sell to Close + STO = 3, // Sell to Open + BTC = 4, // Buy to Close + END = 5, // Exit the trade +} + + +public class TOrders : List<(DateTime t, OType o)> +{ + + public void Add((DateTime t, OType o) TOrder, bool update = false) + { + if (update) { this[^1] = TOrder; } + else { base.Add(TOrder); } + OnEvent(update); + } + + + protected virtual void OnEvent(bool update = false) + { + Pub?.Invoke(this, new TSeriesEventArgs { update = update }); + } + public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); + public event NewDataEventHandler Pub; + +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ADL_Series.cs b/archive/Calculations/_Updated/ADL_Series.cs new file mode 100644 index 00000000..9873a1cc --- /dev/null +++ b/archive/Calculations/_Updated/ADL_Series.cs @@ -0,0 +1,79 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ADL: Chaikin Accumulation/Distribution Line + ADL is a volume-based indicator that measures the cumulative Money Flow Volume: + + 1. Money Flow Multiplier = [(Close - Low) - (High - Close)] /(High - Low) + 2. Money Flow Volume = Money Flow Multiplier x Volume for the Period + 3. ADL = Previous ADL + Current Period's Money Flow Volume + +Sources: + https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line + + */ + +public class ADL_Series : TSeries +{ + protected readonly TBars _data; + private double _lastadl, _lastlastadl; + + //core constructors + public ADL_Series() + { + Name = $"ADL()"; + _lastadl = _lastlastadl = 0; + } + public ADL_Series(TBars source) + { + _data = source; + Name = $"ADL({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _lastadl = _lastlastadl = 0; + _data.Pub += Sub; + Add(data: _data); + } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + if (update) { this._lastadl = this._lastlastadl; } + else { this._lastlastadl = this._lastadl; } + + double _adl = 0; + double tmp = TBar.h - TBar.l; + if (tmp > 0.0) + { + _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); + } + _lastadl = _adl; + + var ret = (TBar.t, _adl); + return base.Add(ret, update); + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _lastadl = _lastlastadl = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ADOSC_Series.cs b/archive/Calculations/_Updated/ADOSC_Series.cs new file mode 100644 index 00000000..b3ebc2c1 --- /dev/null +++ b/archive/Calculations/_Updated/ADOSC_Series.cs @@ -0,0 +1,100 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ADOSC: Chaikin Accumulation/Distribution Oscillator + ADO measures the momentum of ADL using the difference between slow (10-day) EMA(ADL) + and fast (3-day) EMA(ADL): + + Chaikin A/D Oscillator is defined as 3-day EMA of ADL minus 10-day EMA of ADL + +Sources: + https://school.stockcharts.com/doku.php?id=technical_indicators:chaikin_oscillator + + */ + +public class ADOSC_Series : TSeries +{ + protected readonly TBars _data; + private readonly double _k1, _k2; + private double _lastema1, _lastlastema1, _lastema2, _lastlastema2; + private double _lastadl, _lastlastadl; + + //core constructors + public ADOSC_Series(int shortPeriod, int longPeriod, bool useNaN = false) + { + Name = $"ADOSC()"; + _k1 = 2.0 / (shortPeriod + 1); + _k2 = 2.0 / (longPeriod + 1); + _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; + } + public ADOSC_Series(TBars source, int shortPeriod, int longPeriod, bool useNaN = false) : this(shortPeriod, longPeriod, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _lastadl = _lastlastadl = 0; + _data.Pub += Sub; + Add(data: _data); + } + + public ADOSC_Series() : this(shortPeriod: 3, longPeriod: 10, useNaN: false) { } + + public ADOSC_Series(TBars source) : this(source, shortPeriod: 3, longPeriod: 10, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + + if (update) + { + _lastadl = _lastlastadl; + _lastema1 = _lastlastema1; + _lastema2 = _lastlastema2; + } + + double _adl = 0; + double tmp = TBar.h - TBar.l; + if (tmp > 0.0) { _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); } + if (this.Count == 0) { _lastema1 = _lastema2 = _adl; } + + double _ema1 = (_adl - _lastema1) * _k1 + _lastema1; + double _ema2 = (_adl - _lastema2) * _k2 + _lastema2; + + _lastlastadl = _lastadl; + _lastadl = _adl; + _lastlastema1 = _lastema1; + _lastema1 = _ema1; + _lastlastema2 = _lastema2; + _lastema2 = _ema2; + + double _adosc = _ema1 - _ema2; + + var ret = (TBar.t, _adosc); + return base.Add(ret, update); + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ALMA_Series.cs b/archive/Calculations/_Updated/ALMA_Series.cs new file mode 100644 index 00000000..5c62c0dd --- /dev/null +++ b/archive/Calculations/_Updated/ALMA_Series.cs @@ -0,0 +1,129 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +ALMA: Arnaud Legoux Moving Average + The ALMA moving average uses the curve of the Normal (Gauss) distribution, which + can be shifted from 0 to 1. This allows regulating the smoothness and high + sensitivity of the indicator. Sigma is another parameter that is responsible for + the shape of the curve coefficients. This moving average reduces lag of the data + in conjunction with smoothing to reduce noise. + + +Sources: + https://phemex.com/academy/what-is-arnaud-legoux-moving-averages + https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/ + + Discrepancy with Pandas-TA (but passes the validation with Skender.GetAlma) + */ + +public class ALMA_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + private readonly System.Collections.Generic.List _buffer = new(); + private readonly System.Collections.Generic.List _weight; + private double _norm; + private readonly double _offset, _sigma; + + //core constructors + public ALMA_Series(int period, double offset, double sigma, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"ALMA({period})"; + _offset = offset; + _sigma = sigma; + _weight = new(); + } + public ALMA_Series(TSeries source, int period, double offset, double sigma, bool useNaN) : this(period, offset, sigma, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + public ALMA_Series() : this(period: 0, offset: 0.85, sigma: 6.0, useNaN: false) { } + public ALMA_Series(int period) : this(period: period, offset: 0.85, sigma: 6.0, useNaN: false) { } + public ALMA_Series(TBars source) : this(source: source.Close, period: 0, offset: 0.85, sigma: 6.0, useNaN: false) { } + public ALMA_Series(TBars source, int period) : this(source: source.Close, period: period, offset: 0.85, sigma: 6.0, useNaN: false) { } + public ALMA_Series(TBars source, int period, double offset, double sigma, bool useNaN) : this(source.Close, period: period, offset: offset, sigma: sigma, useNaN: false) { } + public ALMA_Series(TSeries source) : this(source, period: 0, offset: 0.85, sigma: 6.0, useNaN: false) { } + public ALMA_Series(TSeries source, int period) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: false) { } + public ALMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: useNaN) { } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, double.NaN), update); + } + + BufferTrim(_buffer, TValue.v, _period, update); + if (_weight.Count < _buffer.Count) + { + for (var i = 0; i < _buffer.Count - _weight.Count; i++) + { + _weight.Add(0.0); + } + } + + + if (_buffer.Count <= _period || _period == 0) + { + var _len = _buffer.Count; + _norm = 0; + var _m = _offset * (_len - 1); + var _s = _len / _sigma; + for (var i = 0; i < _len; i++) + { + var _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s)); + _weight[i] = _wt; + _norm += _wt; + } + } + + double _weightedSum = 0; + for (var i = 0; i < _buffer.Count; i++) + { + _weightedSum += _weight[i] * _buffer[i]; + } + + var _alma = _weightedSum / _norm; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + //reset calculation + public override void Reset() + { + _buffer.Clear(); + _weight.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ATRP_Series.cs b/archive/Calculations/_Updated/ATRP_Series.cs new file mode 100644 index 00000000..9a4fee50 --- /dev/null +++ b/archive/Calculations/_Updated/ATRP_Series.cs @@ -0,0 +1,97 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ATRP: Average True Range Percent + Average True Range Percent is (ATR/Close Price)*100. + This normalizes so it can be compared to other stocks. + +Sources: + https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atrp + + */ + +public class ATRP_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private double _k; + private int _len; + private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; + + //core constructors + public ATRP_Series(int period, bool useNaN) + { + _period = period; + _k = 1.0 / (double)(_period); + _NaN = useNaN; + _len = 0; + Name = $"ATRP({period})"; + } + public ATRP_Series(TBars source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public ATRP_Series() : this(period: 1, useNaN: false) { } + public ATRP_Series(int period) : this(period: period, useNaN: false) { } + public ATRP_Series(TBars source) : this(source, period: 1, useNaN: false) { } + public ATRP_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } + else + { + _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; + _k = (_period == 0) ? 1 / (double)_len : _k; + _len++; + } + + if (_len == 1) { _cm1 = TBar.c; } + double d1 = Math.Abs(TBar.h - TBar.l); + double d2 = Math.Abs(_cm1 - TBar.h); + double d3 = Math.Abs(_cm1 - TBar.l); + (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); + _cm1 = TBar.c; + + double _atr = 0; + if (this.Count == 0) { _atr = d.v; } + else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } + else { _atr = _k * (d.v - _lastatr) + _lastatr; } + _lastatr = _atr; + double _atrp = 100 * (_atr / TBar.c); + + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atrp); + return base.Add(res, update); + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ATR_Series.cs b/archive/Calculations/_Updated/ATR_Series.cs new file mode 100644 index 00000000..c7e36b0a --- /dev/null +++ b/archive/Calculations/_Updated/ATR_Series.cs @@ -0,0 +1,98 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ATR: wildeR Moving Average + The average true range (ATR) is a price volatility indicator + showing the average price variation of assets within a given time period. + +Sources: + https://en.wikipedia.org/wiki/Average_true_range + https://www.tradingview.com/wiki/Average_True_Range_(ATR) + https://www.investopedia.com/terms/a/atr.asp + + */ + +public class ATR_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private double _k; + private int _len; + private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum; + + //core constructors + public ATR_Series(int period, bool useNaN) + { + _period = period; + _k = 1.0 / (double)(_period); + _NaN = useNaN; + _len = 0; + Name = $"ATR({period})"; + } + public ATR_Series(TBars source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public ATR_Series() : this(period: 1, useNaN: false) { } + public ATR_Series(int period) : this(period: period, useNaN: false) { } + public ATR_Series(TBars source) : this(source, period: 1, useNaN: false) { } + public ATR_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; } + else + { + _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; + _k = (_period == 0) ? 1 / (double)_len : _k; + _len++; + } + + if (_len == 1) { _cm1 = TBar.c; } + double d1 = Math.Abs(TBar.h - TBar.l); + double d2 = Math.Abs(_cm1 - TBar.h); + double d3 = Math.Abs(_cm1 - TBar.l); + (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); + _cm1 = TBar.c; + + double _atr = 0; + if (this.Count == 0) { _atr = d.v; } + else if (this.Count < _period + 1) { _sum += d.v; _atr = _sum / (this.Count); } + else { _atr = _k * (d.v - _lastatr) + _lastatr; } + _lastatr = _atr; + + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _atr); + return base.Add(res, update); + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/BBANDS_Series.cs b/archive/Calculations/_Updated/BBANDS_Series.cs new file mode 100644 index 00000000..1750def4 --- /dev/null +++ b/archive/Calculations/_Updated/BBANDS_Series.cs @@ -0,0 +1,121 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +BBANDS: Bollinger Bands® + Price channels created by John Bollinger, depict volatility as standard deviation boundary + line range from a moving average of price. The bands automatically widen when volatility + increases and contract when volatility decreases. Their dynamic nature allows them to be + used on different securities with the standard settings. + + Mid Band = simple moving average (SMA) + Upper Band = SMA + (standard deviation of price x multiplier) + Lower Band = SMA - (standard deviation of price x multiplier) + Bandwidth = Width of the channel: (Upper-Lower)/SMA + %B = The location of the data point within the channel: (Price-Lower)/(Upper/Lower) + Z-Score = number of standard deviations of the data point from SMA + +Sources: + https://www.investopedia.com/terms/b/bollingerbands.asp + https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands + +Note: + Bollinger Bands® is a registered trademark of John A. Bollinger. + + */ + +public class BBANDS_Series : TSeries +{ + protected readonly int _period; + protected readonly double _multiplier; + protected readonly bool _NaN; + protected readonly TSeries _data; + public SMA_Series Mid { get; } + public TSeries Upper { get; } + public TSeries Lower { get; } + public TSeries PercentB { get; } + public TSeries Bandwidth { get; } + public TSeries Zscore { get; } + private readonly SDEV_Series _sdev; + + //core constructors + public BBANDS_Series(int period, double multiplier, bool useNaN) + { + _period = period; + _multiplier = multiplier; + _NaN = useNaN; + Name = $"BBANDS({period})"; + } + public BBANDS_Series(TSeries source, int period, double multiplier, bool useNaN) : this(period, multiplier, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + Upper = new("BB_Up"); + Lower = new("BB_Low"); + Bandwidth = new("BBandwidth"); + PercentB = new("%BBandwidth"); + Zscore = new("Zscore"); + + Mid = new(period, false); + _sdev = new(period, false); + + _data.Pub += Sub; + Add(_data); + } + + public BBANDS_Series() : this(period: 0, multiplier: 2.0, useNaN: false) { } + public BBANDS_Series(int period) : this(period: period, multiplier: 2.0, useNaN: false) { } + public BBANDS_Series(TBars source) : this(source: source.Close, period: 0, multiplier: 2.0, useNaN: false) { } + public BBANDS_Series(TBars source, int period) : this(source: source.Close, period: period, multiplier: 2.0, useNaN: false) { } + public BBANDS_Series(TBars source, int period, double multiplier, bool useNaN) : this(source.Close, period: period, multiplier: multiplier, useNaN: false) { } + public BBANDS_Series(TSeries source) : this(source, period: 0, useNaN: false) { } + public BBANDS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + public BBANDS_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, multiplier: 2.0, useNaN: useNaN) { } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + var _mid = Mid.Add(TValue, update); + var _sd = this._sdev.Add(TValue, update); + var _upper = Upper.Add((TValue.t, _mid.v + _sd.v * _multiplier), update); + var _lower = Lower.Add((TValue.t, _mid.v - _sd.v * _multiplier), update); + double _pbdnd = TValue.v - _lower.v; + double _pbdvr = _upper.v - _lower.v; + PercentB.Add((TValue.t, _pbdnd / _pbdvr), update); + Zscore.Add((TValue.t, (TValue.v - _mid.v) / _sd.v), update); + Bandwidth.Add((TValue.t, _pbdvr / _mid.v), update); + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pbdvr / _mid.v); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + //reset calculation + public override void Reset() + { + Mid.Clear(); + _sdev.Clear(); + Upper.Clear(); + Lower.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/BIAS_Series.cs b/archive/Calculations/_Updated/BIAS_Series.cs new file mode 100644 index 00000000..f8bcb0a3 --- /dev/null +++ b/archive/Calculations/_Updated/BIAS_Series.cs @@ -0,0 +1,81 @@ +namespace QuanTAlib; +using System; + +/* +BIAS: Rate of change between the source and a moving average. + Bias is a statistical term which means a systematic deviation from the actual value. + +BIAS = (close - SMA) / SMA + = (close / SMA) - 1 + +Sources: + https://en.wikipedia.org/wiki/Bias_of_an_estimator + + */ + +public class BIAS_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly SMA_Series _sma; + + //core constructors + public BIAS_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"BIAS({period})"; + _sma = new(period, false); + } + public BIAS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public BIAS_Series() : this(period: 0, useNaN: false) { } + public BIAS_Series(int period) : this(period: period, useNaN: false) { } + public BIAS_Series(TBars source) : this(source.Close, 0, false) { } + public BIAS_Series(TBars source, int period) : this(source.Close, period, false) { } + public BIAS_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public BIAS_Series(TSeries source) : this(source, 0, false) { } + public BIAS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + var _s = _sma.Add(TValue, update); + double _bias = (TValue.v / ((_s.v != 0) ? _s.v : 1)) - 1; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _bias); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _sma.Reset(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/CCI_Series.cs b/archive/Calculations/_Updated/CCI_Series.cs new file mode 100644 index 00000000..4ee2d959 --- /dev/null +++ b/archive/Calculations/_Updated/CCI_Series.cs @@ -0,0 +1,97 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +CCI: Commodity Channel Index + Commodity Channel Index is a momentum oscillator used to primarily identify overbought + and oversold levels relative to a mean. CCI measures the current price level relative + to an average price level over a given period of time: + - CCI is relatively high when prices are far above their average. + - CCI is relatively low when prices are far below their average. + Using this method, CCI can be used to identify overbought and oversold levels. + +Sources: + https://www.investopedia.com/terms/c/commoditychannelindex.asp + https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci + + */ + +public class CCI_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private readonly System.Collections.Generic.List _tp = new(); + + //core constructors + public CCI_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"CCI({period})"; + } + public CCI_Series(TBars source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public CCI_Series() : this(period: 2, useNaN: false) { } + public CCI_Series(int period) : this(period: period, useNaN: false) { } + public CCI_Series(TBars source) : this(source, period: 2, useNaN: false) { } + public CCI_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + double _tpItem = (TBar.h + TBar.l + TBar.c) / 3.0; + if (update) + { + this._tp[this._tp.Count - 1] = _tpItem; + } + else + { + this._tp.Add(_tpItem); + } + if (this._tp.Count > this._period) { this._tp.RemoveAt(0); } + + // average TP over _tp buffer + double _avgTp = _tp.Average(); + + // average Deviation over _tp buffer + double _avgDv = 0; + for (int i = 0; i < this._tp.Count; i++) { _avgDv += Math.Abs(_avgTp - this._tp[i]); } + _avgDv /= this._tp.Count; + + double _cci = (_avgDv == 0) ? 0 : (this._tp[this._tp.Count - 1] - _avgTp) / (0.015 * _avgDv); + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _cci); + return base.Add(res, update); + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _tp.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/CMO_Series.cs b/archive/Calculations/_Updated/CMO_Series.cs new file mode 100644 index 00000000..36476603 --- /dev/null +++ b/archive/Calculations/_Updated/CMO_Series.cs @@ -0,0 +1,100 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +CMO: Chande Momentum Oscillator + Chande Momentum Oscillator (also known as CMO indicator) was developed by Tushar S. Chande + CMO is similar to other momentum oscillators (e.g. RSI or Stochastics). Alike RSI oscillator, + the CMO values move in the range from -100 to +100 points and its aim is to detect the + overbought and oversold market conditions. CMO calculates the price momentum on both the up + days as well as the down days. The CMO calculation is based on non-smoothed price values + meaning that it can reach its extremes more frequently and the short-time swings are more visible. + +Sources: + https://www.technicalindicators.net/indicators-technical-analysis/144-cmo-chande-momentum-oscillator + + */ + +public class CMO_Series : TSeries +{ + private readonly System.Collections.Generic.List _buff_up = new(); + private readonly System.Collections.Generic.List _buff_dn = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private double _plast_value, _last_value; + + //core constructors + public CMO_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"CMO({period})"; + } + public CMO_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public CMO_Series() : this(period: 0, useNaN: false) { } + public CMO_Series(int period) : this(period: period, useNaN: false) { } + public CMO_Series(TBars source) : this(source.Close, 0, false) { } + public CMO_Series(TBars source, int period) : this(source.Close, period, false) { } + public CMO_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public CMO_Series(TSeries source) : this(source, 0, false) { } + public CMO_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (update) { _last_value = _plast_value; } else { _plast_value = _last_value; } + BufferTrim(buffer: _buff_up, (TValue.v > _last_value) ? TValue.v - _last_value : 0, period: _period, update: update); + BufferTrim(buffer: _buff_dn, (TValue.v < _last_value) ? _last_value - TValue.v : 0, period: _period, update: update); + _last_value = TValue.v; + double _cmo_up = 0; + double _cmo_dn = 0; + for (int i = 0; i < Math.Min(_buff_up.Count, _buff_dn.Count); i++) + { + _cmo_up += _buff_up[i]; + _cmo_dn += _buff_dn[i]; + } + double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn); + if (_cmo_up + _cmo_dn == 0) { _cmo = 0; } + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _cmo); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buff_up.Clear(); + _buff_dn.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/CUSUM_Series.cs b/archive/Calculations/_Updated/CUSUM_Series.cs new file mode 100644 index 00000000..eef69d44 --- /dev/null +++ b/archive/Calculations/_Updated/CUSUM_Series.cs @@ -0,0 +1,80 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +CUSUM: Cumulative Sum (aka Running Total) + SUM across a period provides a rolling sum of all values across the period. + If SUM values would be divided with period, the output would be SMA() + +Sources: + https://en.wikipedia.org/wiki/CUSUM + */ + +public class CUSUM_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public CUSUM_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"CUSUM({period})"; + } + public CUSUM_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public CUSUM_Series() : this(period: 0, useNaN: false) { } + public CUSUM_Series(int period) : this(period: period, useNaN: false) { } + public CUSUM_Series(TBars source) : this(source.Close, 0, false) { } + public CUSUM_Series(TBars source, int period) : this(source.Close, period, false) { } + public CUSUM_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public CUSUM_Series(TSeries source) : this(source, period: 0, useNaN: false) { } + public CUSUM_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sum = 0; + for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; } + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sum); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/DECAY_Series.cs b/archive/Calculations/_Updated/DECAY_Series.cs new file mode 100644 index 00000000..287b3f45 --- /dev/null +++ b/archive/Calculations/_Updated/DECAY_Series.cs @@ -0,0 +1,93 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +DECAY: + Linear decay can be modeled by a straight line with a negative slope of 1/period. + The value decreases in a straight line from the last maximum to 0. + Decay = Last Max - distance/period + + Exponential decay is modeled as an exponential curve with diminishing factor of + 1-1/p + + */ + +public class DECAY_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly bool _exp; + private double _pdecay, _ppdecay; + private readonly double _dfactor; + + //core constructors + public DECAY_Series(int period, bool exponential, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"DECAY({period})"; + _exp = exponential; + _dfactor = (_exp) ? 1.0 - 1.0 / (double)_period : 1 / (double)_period; + _pdecay = _ppdecay = 0; + } + public DECAY_Series(TSeries source, int period, bool exponential, bool useNaN) : this(period, exponential, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public DECAY_Series() : this(period: 0, exponential: false, useNaN: false) { } + public DECAY_Series(int period) : this(period: period, exponential: false, useNaN: false) { } + public DECAY_Series(TBars source) : this(source.Close, period: 0, exponential: false, useNaN: false) { } + public DECAY_Series(TBars source, int period) : this(source.Close, period: period, exponential: false, useNaN: false) { } + public DECAY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, exponential: false, useNaN) { } + public DECAY_Series(TSeries source) : this(source, period: 0, exponential: false, useNaN: false) { } + public DECAY_Series(TSeries source, int period) : this(source: source, period: period, exponential: false, useNaN: false) { } + public DECAY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, exponential: false, useNaN: useNaN) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + if (update) { _pdecay = _ppdecay; } + else { _ppdecay = _pdecay; } + + if (this.Count == 0) { _pdecay = TValue.v; } + double _decay = Math.Max(TValue.v, Math.Max((_exp) ? _pdecay * _dfactor : _pdecay - _dfactor, 0)); + _pdecay = _decay; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _decay); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _pdecay = _ppdecay = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/DEMA_Series.cs b/archive/Calculations/_Updated/DEMA_Series.cs new file mode 100644 index 00000000..1288d666 --- /dev/null +++ b/archive/Calculations/_Updated/DEMA_Series.cs @@ -0,0 +1,144 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +DEMA: Double Exponential Moving Average + DEMA uses EMA(EMA()) to calculate smoother Exponential moving average. + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/ + +Remark: + ema1 = EMA(close, length) + ema2 = EMA(ema1, length) + DEMA = 2 * ema1 - ema2 + + */ + +public class DEMA_Series : TSeries +{ + private double _k; + private double _sum, _oldsum; + private double _lastema1, _oldema1, _lastema2, _oldema2; + private int _len; + private readonly bool _useSMA; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructor + public DEMA_Series(int period, bool useNaN, bool useSMA) + { + _period = period; + _NaN = useNaN; + _useSMA = useSMA; + Name = $"DEMA({period})"; + _k = 2.0 / (_period + 1); + _len = 0; + _sum = _oldsum = _lastema1 = _lastema2 = 0; + } + //generic constructors (source) + + public DEMA_Series() : this(0, false, true) { } + public DEMA_Series(int period) : this(period, false, true) { } + public DEMA_Series(TBars source) : this(source.Close, 0, false) { } + public DEMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public DEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public DEMA_Series(TSeries source, int period) : this(source, period, false, true) { } + public DEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } + public DEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (update) + { + _lastema1 = _oldema1; + _lastema2 = _oldema2; + _sum = _oldsum; + } + else + { + _oldema1 = _lastema1; + _oldema2 = _lastema2; + _oldsum = _sum; + _len++; + } + + if (_period == 0) + { + _k = 2.0 / (_len + 1); + } + + double _ema1, _ema2, _dema; + if (Count == 0) + { + _ema1 = _ema2 = _sum = TValue.v; + } + else if (_len <= _period && _useSMA && _period != 0) + { + _sum += TValue.v; + _ema1 = _sum / Math.Min(_len, _period); + _ema2 = _ema1; + } + else + { + _ema1 = (TValue.v - _lastema1) * _k + _lastema1; + _ema2 = (_ema1 - _lastema2) * _k + _lastema2; + } + + _dema = 2 * _ema1 - _ema2; + + _lastema1 = double.IsNaN(_ema1) ? _lastema1 : _ema1; + _lastema2 = double.IsNaN(_ema2) ? _lastema2 : _ema2; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dema); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) + { + return (DateTime.Today, double.NaN); + } + + foreach (var item in data) + { + Add(item, false); + } + + return _data.Last; + } + + public (DateTime t, double v) Add(bool update) + { + return Add(_data.Last, update); + } + + public (DateTime t, double v) Add() + { + return Add(_data.Last, false); + } + + private new void Sub(object source, TSeriesEventArgs e) + { + Add(_data.Last, e.update); + } + + //reset calculation + public override void Reset() + { + _sum = _oldsum = _lastema1 = _lastema2 = 0; + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/DWMA_Series.cs b/archive/Calculations/_Updated/DWMA_Series.cs new file mode 100644 index 00000000..7c03b8a9 --- /dev/null +++ b/archive/Calculations/_Updated/DWMA_Series.cs @@ -0,0 +1,143 @@ +namespace QuanTAlib; + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +/* +DWMA: Double Weighted Moving Average + The weights are decreasing over the period with p^2 decay + and the most recent data has the heaviest weight. + + */ + +public class DWMA_Series : TSeries +{ + private readonly List _buffer = new(); + private List _weights; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + protected int _len; + + //core constructors + public DWMA_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"DWMA({period})"; + _len = 0; + _weights = CalculateWeights(_period); + } + + public DWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + public DWMA_Series() : this(0, false) + { + } + + public DWMA_Series(int period) : this(period, false) + { + } + + public DWMA_Series(TBars source) : this(source.Close, 0, false) + { + } + + public DWMA_Series(TBars source, int period) : this(source.Close, period, false) + { + } + + public DWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) + { + } + + public DWMA_Series(TSeries source, int period) : this(source, period, false) + { + } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(_buffer, TValue.v, _period, update); + if (_period == 0) + { + _len++; + _weights = CalculateWeights(_len); + } + + double _dwma = 0, _wsum = 0; + var bufferCount = _buffer.Count; + + var lockObj = new object(); + Parallel.For(0, bufferCount, i => + { + var temp = _buffer[i] * _weights[i]; + lock (lockObj) + { + _dwma += temp; + _wsum += _weights[i]; + } + }); + _dwma /= _wsum; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dwma); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) + { + return (DateTime.Today, double.NaN); + } + + foreach (var item in data) + { + Add(item, false); + } + + return _data.Last; + } + + public (DateTime t, double v) Add(bool update) + { + return Add(_data.Last, update); + } + + public (DateTime t, double v) Add() + { + return Add(_data.Last, false); + } + + private new void Sub(object source, TSeriesEventArgs e) + { + Add(_data.Last, e.update); + } + + //calculating weights + private static List CalculateWeights(int period) + { + var weights = new List(period); + for (var i = 0; i < period; i++) + { + weights.Add((i + 1) * (i + 1)); + } + + return weights; + } + + //reset calculation + public override void Reset() + { + _len = 0; + _buffer.Clear(); + _weights = CalculateWeights(_period); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/EMA_Series.cs b/archive/Calculations/_Updated/EMA_Series.cs new file mode 100644 index 00000000..7f49566d --- /dev/null +++ b/archive/Calculations/_Updated/EMA_Series.cs @@ -0,0 +1,136 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +EMA: Exponential Moving Average + EMA needs very short history buffer and calculates the EMA value using just the + previous EMA value. The weight of the new datapoint (k) is k = 2 / (period-1) + +Sources: + https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages + https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp + https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA + +Issues: + There is no consensus what the first EMA value should be - a zero, a first + datapoint, or an average of the initial Period bars. All three starting methods + converge within 20+ bars to the same moving average. Most implementations (including this one) + use SMA() for the first Period bars as a seeding value for EMA. + + */ + +public class EMA_Series : TSeries +{ + private double _k; + private double _lastema, _oldema; + private double _sum, _oldsum; + private int _len; + private readonly bool _useSMA; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + + public EMA_Series(int period, bool useNaN, bool useSMA) + { + _period = period; + _NaN = useNaN; + _useSMA = useSMA; + Name = $"EMA({period})"; + _k = 2.0 / (_period + 1); + _len = 0; + _sum = _oldsum = _lastema = _oldema = 0; + } + public EMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public EMA_Series() : this(0, false, true) { } + public EMA_Series(int period) : this(period, false, true) { } + public EMA_Series(TBars source) : this(source.Close, 0, false) { } + public EMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public EMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public EMA_Series(TSeries source, int period) : this(source, period, false, true) { } + public EMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } + + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (update) + { + _lastema = _oldema; + _sum = _oldsum; + } + else + { + _oldema = _lastema; + _oldsum = _sum; + _len++; + } + + double _ema = 0; + if (_period == 0) + { + _k = 2.0 / (_len + 1); + } + + if (Count == 0) + { + _ema = _sum = TValue.v; + } + else if (_len <= _period && _useSMA && _period != 0) + { + _sum += TValue.v; + if (_period != 0 && _len > _period) + { + _sum -= _data[Count - _period - (update ? 1 : 0)].v; + } + + _ema = _sum / Math.Min(_len, _period); + } + else + { + _ema = _k * (TValue.v - _lastema) + _lastema; + } + + _lastema = double.IsNaN(_ema) ? _lastema : _ema; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ema); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _sum = _oldsum = _lastema = _oldema = 0; + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ENTROPY_Series.cs b/archive/Calculations/_Updated/ENTROPY_Series.cs new file mode 100644 index 00000000..14da1862 --- /dev/null +++ b/archive/Calculations/_Updated/ENTROPY_Series.cs @@ -0,0 +1,97 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +ENTROPY: + Introduced by Claude Shannon in 1948, entropy measures the unpredictability + of the data, or equivalently, of its average information. + +Calculation: + P = close / Σ(close) + ENTROPY = Σ(-P * Log(P) / Log(base)) + +Sources: + https://en.wikipedia.org/wiki/Entropy_(information_theory) + https://math.stackexchange.com/questions/3428693/how-to-calculate-entropy-from-a-set-of-correlated-samples + + */ + +public class ENTROPY_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly double _logbase; + private readonly System.Collections.Generic.List _buffer = new(); + private readonly System.Collections.Generic.List _buff2 = new(); + + //core constructors + public ENTROPY_Series(int period, double logbase, bool useNaN) + { + _period = period; + _NaN = useNaN; + _logbase = logbase; + Name = $"ENTROPY({period})"; + } + public ENTROPY_Series(TSeries source, int period, double logbase, bool useNaN) : this(period, logbase, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public ENTROPY_Series() : this(period: 0, logbase: 2.0, useNaN: false) { } + public ENTROPY_Series(int period) : this(period: period, logbase: 2.0, useNaN: false) { } + public ENTROPY_Series(TBars source) : this(source.Close, period: 0, logbase: 2.0, useNaN: false) { } + public ENTROPY_Series(TBars source, int period) : this(source.Close, period, logbase: 2.0, useNaN: false) { } + public ENTROPY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, logbase: 2.0, useNaN: useNaN) { } + public ENTROPY_Series(TSeries source) : this(source, period: 0, logbase: 2.0, useNaN: false) { } + public ENTROPY_Series(TSeries source, int period) : this(source: source, period: period, logbase: 2.0, useNaN: false) { } + public ENTROPY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, logbase: 2.0, useNaN: useNaN) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + double _sum = _buffer.Sum(); + double _pp = this._buffer[^1] / _sum; + double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase); + BufferTrim(_buff2, _ppp, _period, update); + double _entp = _buff2.Sum(); + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _entp); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + _buff2.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/FWMA_Series.cs b/archive/Calculations/_Updated/FWMA_Series.cs new file mode 100644 index 00000000..eceecc6b --- /dev/null +++ b/archive/Calculations/_Updated/FWMA_Series.cs @@ -0,0 +1,105 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Numerics; +using System.Linq; + +/* +FWMA: Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average + (WMA) where the weights are based on the Fibonacci Sequence. + + */ +public class FWMA_Series : TSeries +{ + private readonly List _buffer = new(); + private List _weights; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + protected int _len; + + public FWMA_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"FWMA({period})"; + _len = 0; + _weights = CalculateWeights(_period); + } + + public FWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + public FWMA_Series() : this(period: 0, useNaN: false) { } + public FWMA_Series(int period) : this(period: period, useNaN: false) { } + public FWMA_Series(TBars source) : this(source.Close, 0, false) { } + public FWMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public FWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public FWMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + if (_period == 0) + { + _len++; + _weights = CalculateWeights(_len); + } + double _fwma = 0; + double totalWeights = _weights.Sum(); + object lockObj = new object(); + Parallel.For(0, _buffer.Count, i => + { + double temp = _buffer[i] * _weights[i]; + lock (lockObj) { _fwma += temp; } + }); + _fwma /= totalWeights; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fwma); + return base.Add(res, update); + } + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + private static List CalculateWeights(int period) + { + //to prevent overflow, max period can be no more than 1476 + period = (period > 1476) ? 1476 : period; + List weights = new List(period); + BigInteger a = 0; + BigInteger b = 1; + for (int i = 0; i < period; i++) + { + BigInteger temp = a; + a = b; + b = temp + b; + weights.Add((double)Decimal.Parse(a.ToString())); + } + return weights; + } + + public override void Reset() + { + _weights = CalculateWeights(_period); + _buffer.Clear(); + } +} diff --git a/archive/Calculations/_Updated/HEMA_Series.cs b/archive/Calculations/_Updated/HEMA_Series.cs new file mode 100644 index 00000000..0d45962f --- /dev/null +++ b/archive/Calculations/_Updated/HEMA_Series.cs @@ -0,0 +1,133 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +HEMA: Hull-EMA Moving Average - a hybrid indicator + Modified HUll Moving Average; instead of using WMA (Weighted MA) for calculation, + HEMA uses EMA for Hull's formula: + +EMA1 = EMA(n/2) of price - where k = 4/(n/2 +1) +EMA2 = EMA(n) of price - where k = 3/(n+1) +Raw HMA = (2 * EMA1) - EMA2 +EMA3 = EMA(sqrt(n)) of Raw HMA - where k = 2/(sqrt(n)+1) + */ + +public class HEMA_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private double _k1, _k2, _k3; + private int _len; + private double _lastema1, _oldema1; + private double _lastema2, _oldema2; + private double _lasthema, _oldhema; + + //core constructors + public HEMA_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"HEMA({period})"; + (_k1, _k2, _k3) = CalculateK(_period); + _len = 0; + _lastema1 = _oldema1 = _lastema2 = _oldema2 = _lasthema = _oldhema = 0; + } + public HEMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public HEMA_Series() : this(period: 0, useNaN: false) { } + public HEMA_Series(int period) : this(period: period, useNaN: false) { } + public HEMA_Series(TBars source) : this(source.Close, 0, false) { } + public HEMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public HEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public HEMA_Series(TSeries source) : this(source, 0, false) { } + public HEMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (update) + { + _lastema1 = _oldema1; + _lastema2 = _oldema2; + _lasthema = _oldhema; + } + else + { + _oldema1 = _lastema1; + _oldema2 = _lastema2; + _oldhema = _lasthema; + } + double _ema1, _ema2, _hema; + if (_period == 0) + { + _len++; + (_k1, _k2, _k3) = CalculateK(_len); + } + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, double.NaN), update); + } + else if (this.Count == 0) + { + _ema1 = _ema2 = _hema = TValue.v; + } + else + { + _ema1 = _k1 * (TValue.v - _lastema1) + _lastema1; + _ema2 = _k2 * (TValue.v - _lastema2) + _lastema2; + _hema = _k3 * (((2 * _ema1) - _ema2) - _lasthema) + _lasthema; + } + + _lastema1 = _ema1; + _lastema2 = _ema2; + _lasthema = _hema; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hema); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _lastema1 = _lastema2 = _lasthema = 0; + _oldema1 = _oldema2 = _oldhema = 0; + _len = 0; + } + + public static (double k1, double k2, double k3) CalculateK(int len) + { + double k1 = 8 / (double)(len + 7); + double k2 = 3 / (double)(len + 2); + double k3 = 2 / Math.Sqrt(len + 3); + + return (k1, k2, k3); + } + +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/HMA_Series.cs b/archive/Calculations/_Updated/HMA_Series.cs new file mode 100644 index 00000000..30d9087a --- /dev/null +++ b/archive/Calculations/_Updated/HMA_Series.cs @@ -0,0 +1,98 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +HMA: Hull Moving Average + Developed by Alan Hull, an extremely fast and smooth moving average; almost + eliminates lag altogether and manages to improve smoothing at the same time. + +Sources: + https://alanhull.com/hull-moving-average + https://school.stockcharts.com/doku.php?id=technical_indicators:hull_moving_average + +WMA1 = WMA(n/2) of price +WMA2 = WMA(n) of price +Raw HMA = (2 * WMA1) - WMA2 +HMA = WMA(sqrt(n)) of Raw HMA + + */ + +public class HMA_Series : TSeries +{ + protected int _period, _period2, _psqrt; + protected readonly bool _NaN; + protected readonly TSeries _data; + protected WMA_Series _wma1, _wma2, _wma3; + + //core constructors + public HMA_Series(int period, bool useNaN) + { + _period = period; + _period2 = period / 2; + _psqrt = (int)Math.Sqrt(period); + _NaN = useNaN; + _wma1 = new(Math.Max(_period2, 1), false); + _wma2 = new(Math.Max(_period, 1), false); + _wma3 = new(Math.Max(_psqrt, 1), useNaN); + Name = $"HMA({period})"; + } + public HMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public HMA_Series() : this(period: 0, useNaN: false) { } + public HMA_Series(int period) : this(period: period, useNaN: false) { } + public HMA_Series(TBars source) : this(source.Close, 0, false) { } + public HMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public HMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public HMA_Series(TSeries source) : this(source, 0, false) { } + public HMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (_period == 0) + { + _wma1.Len = this.Count / 2; + _wma2.Len = this.Count; + _wma1.Len = (int)Math.Sqrt(this.Count); + } + double _w1 = _wma1.Add(TValue, update).v; + double _w2 = _wma2.Add(TValue, update).v; + double _hma = _wma3.Add((2 * _w1) - _w2, update).v; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hma); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _wma1.Reset(); + _wma2.Reset(); + _wma3.Reset(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/HWMA_Series.cs b/archive/Calculations/_Updated/HWMA_Series.cs new file mode 100644 index 00000000..b58ea0b2 --- /dev/null +++ b/archive/Calculations/_Updated/HWMA_Series.cs @@ -0,0 +1,146 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +HWMA: Holt-Winter Moving Average + Indicator HWMA (Holt-Winter Moving Average) is a three-parameter moving + average by the Holt-Winter method; Holt-Winters Exponential Smoothing is + used for forecasting time series data that exhibits both a trend and a + seasonal variation. + + +Sources: + https://timeseriesreasoning.com/contents/holt-winters-exponential-smoothing/ + https://www.mql5.com/en/code/20856 + +nA - smoothed series (from 0 to 1) +nB - assess the trend (from 0 to 1) +nC - assess seasonality (from 0 to 1) + +Heuristic for determining alpha, beta, and gamma from period: + alpha = 2 / (1 + period) + beta = 1 / period + gamma = 1 / period + +F[i] = (1-nA) * (F[i-1] + V[i-1] + 0.5 * A[i-1]) + nA * Price[i] +V[i] = (1-nB) * (V[i-1] + A[i-1]) + nB * (F[i] - F[i-1]) +A[i] = (1-nC) * A[i-1] + nC * (V[i] - V[i-1]) +HWMA[i] = F[i] + V[i] + 0.5 * A[i] + + */ + +public class HWMA_Series : TSeries +{ + private int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + double _nA, _nB, _nC; + double _pF, _pV, _pA; + double _ppF, _ppV, _ppA; + + //core constructors + + public HWMA_Series(double nA, double nB, double nC, bool useNaN) + { + _period = (int)((2 - nA) / nA); + _nA = nA; + _nB = nB; + _nC = nC; + _NaN = useNaN; + Name = $"HWMA({_period})"; + _len = 0; + } + public HWMA_Series(TSeries source, double nA, double nB, double nC, bool useNaN = false) : this(nA, nB, nC, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public HWMA_Series() : this(period: 0, useNaN: false) { } + public HWMA_Series(int period) : this(period, useNaN: false) { } + public HWMA_Series(int period, bool useNaN) : this(nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN) + { + _period = period; + } + public HWMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } + public HWMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public HWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public HWMA_Series(TSeries source, int period) : this(source, period, false) { } + public HWMA_Series(TSeries source, int period, bool useNaN) : this(source, nA: 2 / (1 + (double)period), nB: 1 / (double)period, nC: 1 / (double)period, useNaN: useNaN) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + double _F, _V, _A; + if (_len == 0) { _pF = TValue.v; _pA = _pV = 0; } + + if (update) { _pF = _ppF; _pV = _ppV; _pA = _ppA; } + else + { + _ppF = _pF; + _ppV = _pV; + _ppA = _pA; + _len++; + } + + if (_period == 0) + { + _nA = 2 / (1 + (double)_len); + _nB = 1 / (double)_len; + _nC = 1 / (double)_len; + } + if (_period == 1) + { + _nA = 1; + _nB = 0; + _nC = 0; + } + + _F = (1 - _nA) * (_pF + _pV + 0.5 * _pA) + _nA * TValue.v; + _V = (1 - _nB) * (_pV + _pA) + _nB * (_F - _pF); + _A = (1 - _nC) * _pA + _nC * (_V - _pV); + + double _hwma = _F + _V + 0.5 * _A; + _pF = _F; + _pV = _V; + _pA = _A; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hwma); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _len = 0; + } +} \ No newline at end of file diff --git a/Calculations/_Updated/JMA_Series.cs b/archive/Calculations/_Updated/JMA_Series.cs similarity index 97% rename from Calculations/_Updated/JMA_Series.cs rename to archive/Calculations/_Updated/JMA_Series.cs index 517bbbe3..8d5b9af0 100644 --- a/Calculations/_Updated/JMA_Series.cs +++ b/archive/Calculations/_Updated/JMA_Series.cs @@ -1,176 +1,176 @@ -namespace QuanTAlib; -using System; -using System.Collections.Generic; -using System.Linq; - -/* -JMA: Jurik Moving Average - Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the - underlying activity. It has extremely low lag, is very smooth and is responsive - to market gaps. - -Sources: - https://c.mql5.com/forextsd/forum/164/jurik_1.pdf - https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/ - -Issues: - Real JMA algorithm is not published and this formula is derived through - deduction and reverse analysis of JMA behavior. It is really close, but not - exact - published JMA tests against JMA.CSV fail with small deviation. The - original algo is slightly different, yet this approximation is close enough. - - */ - -public class JMA_Series : TSeries -{ - protected readonly int _period; - protected readonly bool _NaN; - protected readonly TSeries _data; - private readonly System.Collections.Generic.List volty_short = new(); - private readonly System.Collections.Generic.List vsum_buff = new(); - private readonly double pr; - private double upperBand, lowerBand, vsum, Kv; - private double prev_ma1, prev_det0, prev_det1, prev_vsum, prev_jma; - private double p_upperBand, p_lowerBand, p_Kv, p_prev_ma1, p_prev_det0, p_prev_det1, p_prev_vsum, p_prev_jma; - private readonly int _voltyS, _voltyL; - - //core constructors - public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) - { - _period = period; - _NaN = useNaN; - Name = $"JMA({period})"; - upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0; - pr = (phase * 0.01) + 1.5; - if (phase < -100) { pr = 0.5; } - if (phase > 100) { pr = 2.5; } - _voltyS = vshort; - _voltyL = vlong; - } - - public JMA_Series(TSeries source, int period, double phase, int vshort, int vlong, bool useNaN) : this(period, phase, vshort, vlong, useNaN) - { - _data = source; - Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; - _data.Pub += Sub; - Add(_data); - } - public JMA_Series() : this(period: 0, phase: 0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(int period) : this(period: period, phase: 0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TBars source) : this(source.Close, period: 0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TBars source, int period) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { } - public JMA_Series(TSeries source) : this(source, period: 0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TSeries source, int period) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } - public JMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { } - - ////////////////// - // core Add() algo - public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) - { - if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; } - if (update) - { - upperBand = p_upperBand; - lowerBand = p_lowerBand; - Kv = p_Kv; - prev_vsum = p_prev_vsum; - prev_ma1 = p_prev_ma1; - prev_det0 = p_prev_det0; - prev_det1 = p_prev_det1; - prev_jma = p_prev_jma; - } - else - { - p_upperBand = upperBand; - p_lowerBand = lowerBand; - p_Kv = Kv; - p_prev_vsum = prev_vsum; - p_prev_ma1 = prev_ma1; - p_prev_det0 = prev_det0; - p_prev_det1 = prev_det1; - p_prev_jma = prev_jma; - } - - if (double.IsNaN(TValue.v)) - { - return base.Add((TValue.t, double.NaN), update); - } - - // from Tvalue to volty - double del1 = TValue.v - upperBand; - double del2 = TValue.v - lowerBand; - upperBand = (del1 > 0) ? TValue.v : TValue.v - (Kv * del1); - lowerBand = (del2 < 0) ? TValue.v : TValue.v - (Kv * del2); - double volty = Math.Abs(del1) > Math.Abs(del2) ? Math.Abs(del1) : - (Math.Abs(del1) < Math.Abs(del2) ? Math.Abs(del2) : - Math.Abs(0.5 * (del1 + del2))); - - //// from volty to avolty - if (update) { volty_short[volty_short.Count - 1] = volty; } - else { volty_short.Add(volty); } - if (volty_short.Count > _voltyS) { volty_short.RemoveAt(0); } - vsum = prev_vsum + 0.1 * (volty - volty_short.First()); - prev_vsum = vsum; - if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; } - else { vsum_buff.Add(vsum); } - if (vsum_buff.Count > _voltyL) { vsum_buff.RemoveAt(0); } - double avolty = 0; - for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; } - avolty /= vsum_buff.Count; - - /// from avolty to rolty - double rvolty = (avolty != 0) ? volty / avolty : 0; - double len1 = (Math.Log(Math.Sqrt(_period)) / Math.Log(2.0)) + 2; - if (len1 < 0) { len1 = 0; } - - double pow1 = Math.Max(len1 - 2.0, 0.5); - if (rvolty > Math.Pow(len1, 1.0 / pow1)) { rvolty = Math.Pow(len1, 1.0 / pow1); } - if (rvolty < 1) { rvolty = 1; } - - //// from rvolty to second smoothing - double pow2 = Math.Pow(rvolty, pow1); - double beta = 0.45 * (_period - 1) / (0.45 * (_period - 1) + 2); - Kv = Math.Pow(beta, Math.Sqrt(pow2)); - double alpha = Math.Pow(beta, pow2); - double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1; - prev_ma1 = ma1; - - double det0 = (1 - beta) * (TValue.v - ma1) + beta * prev_det0; - prev_det0 = det0; - double ma2 = ma1 + pr * det0; - - double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - prev_jma)) + (alpha * alpha * prev_det1); - prev_det1 = det1; - double jma = prev_jma + det1; - prev_jma = jma; - - var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : jma); - return base.Add(res, update); - } - - public override (DateTime t, double v) Add(TSeries data) - { - if (data == null) { return (DateTime.Today, Double.NaN); } - foreach (var item in data) { Add(item, false); } - return _data.Last; - } - public (DateTime t, double v) Add(bool update) - { - return this.Add(TValue: _data.Last, update: update); - } - public (DateTime t, double v) Add() - { - return Add(TValue: _data.Last, update: false); - } - private new void Sub(object source, TSeriesEventArgs e) - { - Add(TValue: _data.Last, update: e.update); - } - - //reset calculation - public override void Reset() - { - upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0; - } +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +JMA: Jurik Moving Average + Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the + underlying activity. It has extremely low lag, is very smooth and is responsive + to market gaps. + +Sources: + https://c.mql5.com/forextsd/forum/164/jurik_1.pdf + https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/ + +Issues: + Real JMA algorithm is not published and this formula is derived through + deduction and reverse analysis of JMA behavior. It is really close, but not + exact - published JMA tests against JMA.CSV fail with small deviation. The + original algo is slightly different, yet this approximation is close enough. + + */ + +public class JMA_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly System.Collections.Generic.List volty_short = new(); + private readonly System.Collections.Generic.List vsum_buff = new(); + private readonly double pr; + private double upperBand, lowerBand, vsum, Kv; + private double prev_ma1, prev_det0, prev_det1, prev_vsum, prev_jma; + private double p_upperBand, p_lowerBand, p_Kv, p_prev_ma1, p_prev_det0, p_prev_det1, p_prev_vsum, p_prev_jma; + private readonly int _voltyS, _voltyL; + + //core constructors + public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"JMA({period})"; + upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0; + pr = (phase * 0.01) + 1.5; + if (phase < -100) { pr = 0.5; } + if (phase > 100) { pr = 2.5; } + _voltyS = vshort; + _voltyL = vlong; + } + + public JMA_Series(TSeries source, int period, double phase, int vshort, int vlong, bool useNaN) : this(period, phase, vshort, vlong, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public JMA_Series() : this(period: 0, phase: 0, vshort: 10, vlong: 65, useNaN: false) { } + public JMA_Series(int period) : this(period: period, phase: 0, vshort: 10, vlong: 65, useNaN: false) { } + public JMA_Series(TBars source) : this(source.Close, period: 0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } + public JMA_Series(TBars source, int period) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } + public JMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { } + public JMA_Series(TSeries source) : this(source, period: 0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } + public JMA_Series(TSeries source, int period) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { } + public JMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; } + if (update) + { + upperBand = p_upperBand; + lowerBand = p_lowerBand; + Kv = p_Kv; + prev_vsum = p_prev_vsum; + prev_ma1 = p_prev_ma1; + prev_det0 = p_prev_det0; + prev_det1 = p_prev_det1; + prev_jma = p_prev_jma; + } + else + { + p_upperBand = upperBand; + p_lowerBand = lowerBand; + p_Kv = Kv; + p_prev_vsum = prev_vsum; + p_prev_ma1 = prev_ma1; + p_prev_det0 = prev_det0; + p_prev_det1 = prev_det1; + p_prev_jma = prev_jma; + } + + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, double.NaN), update); + } + + // from Tvalue to volty + double del1 = TValue.v - upperBand; + double del2 = TValue.v - lowerBand; + upperBand = (del1 > 0) ? TValue.v : TValue.v - (Kv * del1); + lowerBand = (del2 < 0) ? TValue.v : TValue.v - (Kv * del2); + double volty = Math.Abs(del1) > Math.Abs(del2) ? Math.Abs(del1) : + (Math.Abs(del1) < Math.Abs(del2) ? Math.Abs(del2) : + Math.Abs(0.5 * (del1 + del2))); + + //// from volty to avolty + if (update) { volty_short[volty_short.Count - 1] = volty; } + else { volty_short.Add(volty); } + if (volty_short.Count > _voltyS) { volty_short.RemoveAt(0); } + vsum = prev_vsum + 0.1 * (volty - volty_short.First()); + prev_vsum = vsum; + if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; } + else { vsum_buff.Add(vsum); } + if (vsum_buff.Count > _voltyL) { vsum_buff.RemoveAt(0); } + double avolty = 0; + for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; } + avolty /= vsum_buff.Count; + + /// from avolty to rolty + double rvolty = (avolty != 0) ? volty / avolty : 0; + double len1 = (Math.Log(Math.Sqrt(_period)) / Math.Log(2.0)) + 2; + if (len1 < 0) { len1 = 0; } + + double pow1 = Math.Max(len1 - 2.0, 0.5); + if (rvolty > Math.Pow(len1, 1.0 / pow1)) { rvolty = Math.Pow(len1, 1.0 / pow1); } + if (rvolty < 1) { rvolty = 1; } + + //// from rvolty to second smoothing + double pow2 = Math.Pow(rvolty, pow1); + double beta = 0.45 * (_period - 1) / (0.45 * (_period - 1) + 2); + Kv = Math.Pow(beta, Math.Sqrt(pow2)); + double alpha = Math.Pow(beta, pow2); + double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1; + prev_ma1 = ma1; + + double det0 = (1 - beta) * (TValue.v - ma1) + beta * prev_det0; + prev_det0 = det0; + double ma2 = ma1 + pr * det0; + + double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - prev_jma)) + (alpha * alpha * prev_det1); + prev_det1 = det1; + double jma = prev_jma + det1; + prev_jma = jma; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : jma); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0; + } } \ No newline at end of file diff --git a/archive/Calculations/_Updated/KAMA_Series.cs b/archive/Calculations/_Updated/KAMA_Series.cs new file mode 100644 index 00000000..f839d4aa --- /dev/null +++ b/archive/Calculations/_Updated/KAMA_Series.cs @@ -0,0 +1,118 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +KAMA: Kaufman's Adaptive Moving Average + Created in 1988 by American quantitative finance theorist Perry J. Kaufman and is known as + Kaufman's Adaptive Moving Average (KAMA). Even though the method was developed as early as 1972, + it was not until the popular book titled "Trading Systems and Methods" that it was made widely + available to the public. Unlike other conventional moving averages systems, the Kaufman's Adaptive + Moving Average, considers market volatility apart from price fluctuations. + + KAMA[i] = KAMA[i-1] + SC * ( price - KAMA[i-1] ) + +Sources: + https://www.tutorialspoint.com/kaufman-s-adaptive-moving-average-kama-formula-and-how-does-it-work + https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/ + https://www.technicalindicators.net/indicators-technical-analysis/152-kama-kaufman-adaptive-moving-average + +Remark: + If useNaN:true argument is provided, KAMA starts calculating values from [period] bar onwards. + Without useNaN argument (default setting), KAMA starts calculating values from bar 1 - and yields + slightly different results for the first 50 bars - and then converges with the other one. + + */ + +public class KAMA_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + private double _lastkama, _lastlastkama; + private readonly double _scFast, _scSlow; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public KAMA_Series(int period, int fast, int slow, bool useNaN) + { + _period = period; + _NaN = useNaN; + _scFast = 2.0 / (((period < fast) ? period : fast) + 1); + _scSlow = 2.0 / (slow + 1); + _lastkama = _lastlastkama = 0; + Name = $"KAMA({period})"; + } + public KAMA_Series(TSeries source, int period, int fast, int slow, bool useNaN) : this(period, fast, slow, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public KAMA_Series() : this(period: 0, fast: 2, slow: 30, useNaN: false) { } + public KAMA_Series(int period) : this(period: period, fast: 2, slow: 30, useNaN: false) { } + public KAMA_Series(TBars source) : this(source.Close, period: 0, fast: 2, slow: 30, useNaN: false) { } + public KAMA_Series(TBars source, int period) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: false) { } + public KAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: useNaN) { } + public KAMA_Series(TSeries source) : this(source, period: 0, fast: 2, slow: 30, useNaN: false) { } + public KAMA_Series(TSeries source, int period) : this(source: source, period: period, fast: 2, slow: 30, useNaN: false) { } + public KAMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, fast: 2, slow: 30, useNaN: useNaN) { } + public KAMA_Series(TSeries source, int period, int fast, int slow) : this(source: source, period: period, fast: fast, slow: slow, useNaN: false) { } + + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + + if (update) { _lastkama = _lastlastkama; } + else { _lastlastkama = _lastkama; } + BufferTrim(buffer: _buffer, value: TValue.v, period: _period + 1, update: update); + + double _kama = 0; + if (this.Count < _period) { _kama = TValue.v; } + else + { + double _change = Math.Abs(_buffer[^1] - _buffer[(_buffer.Count > _period + 1) ? 1 : 0]); + double _sumpv = 0; + for (int i = 1; i < _buffer.Count; i++) { _sumpv += Math.Abs(_buffer[(_buffer.Count > 0) ? i : 0] - _buffer[i - 1]); } + double _er = (_sumpv == 0) ? 0 : _change / _sumpv; + double _sc = (_er * (_scFast - _scSlow)) + _scSlow; + _kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama))); + } + _lastkama = _kama; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kama); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + _lastkama = _lastlastkama = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/KURTOSIS_Series.cs b/archive/Calculations/_Updated/KURTOSIS_Series.cs new file mode 100644 index 00000000..f632407a --- /dev/null +++ b/archive/Calculations/_Updated/KURTOSIS_Series.cs @@ -0,0 +1,107 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +KURTOSIS: Kurtosis of population + Kurtosis characterizes the relative peakedness or flatness of a distribution + compared with the normal distribution. Positive kurtosis indicates a relatively + peaked distribution. Negative kurtosis indicates a relatively flat distribution. + + The normal curve is called Mesokurtic curve. If the curve of a distribution is + more outlier prone (or heavier-tailed) than a normal or mesokurtic curve then + it is referred to as a Leptokurtic curve. If a curve is less outlier prone (or + lighter-tailed) than a normal curve, it is called as a platykurtic curve. + +Calculation: + sum4 = Σ(close-SMA)^4 + sum2 = (Σ(close-SMA)^2)^2 + KURTOSIS = length * (sum4/sum2) + +Sources: + https://en.wikipedia.org/wiki/Kurtosis + https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ + + */ + +public class KURTOSIS_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly System.Collections.Generic.List _buffer = new(); + + //core constructors + public KURTOSIS_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"KURTOSIS({period})"; + } + public KURTOSIS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public KURTOSIS_Series() : this(period: 0, useNaN: false) { } + public KURTOSIS_Series(int period) : this(period: period, useNaN: false) { } + public KURTOSIS_Series(TSeries source) : this(source, period: 0, useNaN: false) { } + public KURTOSIS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + double _n = _buffer.Count; + double _avg = _buffer.Average(); + + double _s2 = 0; + double _s4 = 0; + for (int i = 0; i < this._buffer.Count; i++) + { + _s2 += (_buffer[i] - _avg) * (_buffer[i] - _avg); + _s4 += (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg); + } + + double _Vx = _s2 / (_n - 1); + double _kurt = (_n > 3) ? + (_n * (_n + 1) * _s4) / (_Vx * _Vx * (_n - 3) * (_n - 1) * (_n - 2)) - (3 * (_n - 1) * (_n - 1) / ((_n - 2) * (_n - 3))) //using Sheskin Algo + : (_s2 * _s2) / _n - 3; //using Snedecor and Cochran (1967) algo + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kurt); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MACD_Series.cs b/archive/Calculations/_Updated/MACD_Series.cs new file mode 100644 index 00000000..c23e1eca --- /dev/null +++ b/archive/Calculations/_Updated/MACD_Series.cs @@ -0,0 +1,88 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MACD: Moving Average Convergence/Divergence + Moving average convergence divergence (MACD) is a trend-following momentum + indicator that shows the relationship between two moving averages of a series. + The MACD is calculated by subtracting the 26-period exponential moving average (EMA) + from the 12-period EMA. MACD Signal is 9-day EMA of MACD. + + */ + +public class MACD_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + + protected readonly int _slow, _fast, _signal; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly EMA_Series _TSlow; + private readonly EMA_Series _TFast; + public EMA_Series Signal { get; } + + //core constructors + public MACD_Series(int slow = 26, int fast = 12, int signal = 9, bool useNaN = false) + { + _slow = slow; + _fast = fast; + _signal = signal; + _NaN = useNaN; + Name = $"MACD({slow},{fast},{signal})"; + _TSlow = new(slow, useNaN: false, useSMA: true); + _TFast = new(fast, useNaN: false, useSMA: true); + Signal = new(signal, useNaN: false, useSMA: true); + } + public MACD_Series(TSeries source, int slow, int fast, int signal, bool useNaN) : this(slow, fast, signal, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MACD_Series(TSeries source) : this(source: source, slow: 26, fast: 12, signal: 9, useNaN: false) { } + public MACD_Series(TSeries source, int slow, int fast, int signal) : this(source: source, slow: slow, fast: fast, signal: signal, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + + var _sslow = _TSlow.Add(TValue, update); + var _sfast = _TFast.Add(TValue, update); + Signal.Add((TValue.t, _sfast.v - _sslow.v)); + + var res = (TValue.t, Count < _fast - 1 && _NaN ? double.NaN : _sfast.v - _sslow.v); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAD_Series.cs b/archive/Calculations/_Updated/MAD_Series.cs new file mode 100644 index 00000000..23a7bf05 --- /dev/null +++ b/archive/Calculations/_Updated/MAD_Series.cs @@ -0,0 +1,88 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MAD: Mean Absolute Deviation + Also known as AAD - Average Absolute Deviation, to differentiate it from Median Absolute Deviation + MAD defines the degree of variation across the series. + +Calculation: + MAD = Σ(|close-SMA|) / period + +Sources: + https://en.wikipedia.org/wiki/Average_absolute_deviation + + */ + +public class MAD_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MAD_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MAD({period})"; + } + public MAD_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MAD_Series() : this(period: 0, useNaN: false) { } + public MAD_Series(int period) : this(period: period, useNaN: false) { } + public MAD_Series(TBars source) : this(source.Close, 0, false) { } + public MAD_Series(TBars source, int period) : this(source.Close, period, false) { } + public MAD_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MAD_Series(TSeries source) : this(source, 0, false) { } + public MAD_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + double _mad = 0; + for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); } + _mad /= this._buffer.Count; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mad); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAE_Series.cs b/archive/Calculations/_Updated/MAE_Series.cs new file mode 100644 index 00000000..566b47c2 --- /dev/null +++ b/archive/Calculations/_Updated/MAE_Series.cs @@ -0,0 +1,86 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MAE: Mean Absolute Error + Defined as a Mean (Average) of the absolute difference between actual and estimated values. + MAE = (1/n) * Σ|y_i - MA_i| + +Sources: + https://en.wikipedia.org/wiki/Mean_absolute_error + + */ + +public class MAE_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MAE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MSE({period})"; + } + public MAE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MAE_Series() : this(period: 0, useNaN: false) { } + public MAE_Series(int period) : this(period: period, useNaN: false) { } + public MAE_Series(TBars source) : this(source.Close, 0, false) { } + public MAE_Series(TBars source, int period) : this(source.Close, period, false) { } + public MAE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MAE_Series(TSeries source) : this(source, 0, false) { } + public MAE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _mae = 0; + for (int i = 0; i < _buffer.Count; i++) { _mae += Math.Abs(_buffer[i] - _sma); } + _mae /= this._buffer.Count; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mae); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAMA_Series.cs b/archive/Calculations/_Updated/MAMA_Series.cs new file mode 100644 index 00000000..538f66a2 --- /dev/null +++ b/archive/Calculations/_Updated/MAMA_Series.cs @@ -0,0 +1,207 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +MAMA: MESA Adaptive Moving Average + Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of + high/low price that uses classic electrical radio-frequency signal processing algorithms + to reduce noise. + + KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 ) + +Sources: + https://mesasoftware.com/papers/MAMA.pdf + https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/ + + */ + +public class MAMA_Series : TSeries +{ + private int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + private double sumPr; + private double fastl, slowl; + private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt; + private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama; + public TSeries Fama { get; } + private double mamaseed, famaseed; + + //core constructors + + public MAMA_Series(double fastlimit, double slowlimit, bool useNaN) + { + _period = (int)(2 / fastlimit) - 1; + fastl = fastlimit; + slowl = slowlimit; + Fama = new TSeries(); + _NaN = useNaN; + Name = $"MAMA({_period})"; + _len = 0; + } + public MAMA_Series(TSeries source, double fastlimit, double slowlimit, bool useNaN = false) : this(fastlimit, slowlimit, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MAMA_Series() : this(period: 0, useNaN: false) { } + public MAMA_Series(int period) : this(period, useNaN: false) { } + public MAMA_Series(int period, bool useNaN) : this(fastlimit: 2 / (period + 1), slowlimit: 0.2 / (period + 1), useNaN) + { + _period = period; + } + public MAMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { } + public MAMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public MAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MAMA_Series(TSeries source, int period) : this(source, period, false) { } + public MAMA_Series(TSeries source, int period, bool useNaN) : this(source, fastlimit: 2 / ((double)period + 1), slowlimit: 0.2 / ((double)period + 1), useNaN: useNaN) { } + + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + if (!update) + { + // roll forward (oldx = x) + pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i; + i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i; + q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i; + dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i; + sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i; + i2.io = i2.i1; i2.i1 = i2.i; q2.io = q2.i1; q2.i1 = q2.i; + re.io = re.i1; re.i1 = re.i; im.io = im.i1; im.i1 = im.i; + pd.io = pd.i1; pd.i1 = pd.i; ph.io = ph.i1; ph.i1 = ph.i; + mama.io = mama.i1; mama.i1 = mama.i; + fama.io = fama.i1; + fama.i1 = fama.i; + _len++; + } + if (_period == 0) + { + fastl = 2 / (double)_len; + slowl = fastl * 0.1; + } + if (_period == 1) + { + fastl = 1; + slowl = 1; + } + var i = _len - 1; + pr.i = TValue.v; + if (i > 5) + { + var adj = 0.075 * pd.i1 + 0.54; + + // smooth and detrender + sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10; + dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj; + + // in-phase and quadrature + q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj; + i1.i = dt.i3; + + // advance the phases by 90 degrees + double jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj; + double jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj; + + // phasor addition for 3-bar averaging + i2.i = i1.i - jQ; + q2.i = q1.i + jI; + + i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it + q2.i = 0.2 * q2.i + 0.8 * q2.i1; + + // homodyne discriminator + re.i = i2.i * i2.i1 + q2.i * q2.i1; + im.i = i2.i * q2.i1 - q2.i * i2.i1; + + re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it + im.i = 0.2 * im.i + 0.8 * im.i1; + + // calculate period + pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d; + + // adjust period to thresholds + pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i; + pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i; + pd.i = pd.i < 6d ? 6d : pd.i; + pd.i = pd.i > 50d ? 50d : pd.i; + + // smooth the period + pd.i = 0.2 * pd.i + 0.8 * pd.i1; + + // determine phase position + ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0; + + // change in phase + var delta = Math.Max(ph.i1 - ph.i, 1d); + + // adaptive alpha value + var alpha = Math.Max(fastl / delta, slowl); + + // final indicators + mama.i = alpha * (pr.i - mama.i1) + mama.i1; + fama.i = 0.5d * alpha * (mama.i - fama.i1) + fama.i1; + } + else + { + sumPr += pr.i; + pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0; + mama.i = fama.i = sumPr / (i + 1); + + if (_len == 1) + { + mamaseed = famaseed = TValue.v; + } + else + { + mamaseed = fastl * (TValue.v - mamaseed) + mamaseed; + famaseed = slowl * (TValue.v - famaseed) + famaseed; + } + } + + double _fama = (i > 5) ? fama.i : famaseed; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fama); + Fama.Add(res, update); + double _mama = (i > 5) ? mama.i : mamaseed; + res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mama); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAPE_Series.cs b/archive/Calculations/_Updated/MAPE_Series.cs new file mode 100644 index 00000000..0ac76546 --- /dev/null +++ b/archive/Calculations/_Updated/MAPE_Series.cs @@ -0,0 +1,95 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MAPE: Mean Absolute Percentage Error + Measures the size of the error in percentage terms + +Calculation: + MAPE = Σ(|close – SMA| / |close|) / n + +Sources: + https://en.wikipedia.org/wiki/Mean_absolute_percentage_error + +Remark: + returns infinity if any of observations is 0. + Use SMAPE or WMAPE instead to avoid division-by-zero in MAPE + + */ + +public class MAPE_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MAPE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MAPE({period})"; + } + public MAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MAPE_Series() : this(period: 0, useNaN: false) { } + public MAPE_Series(int period) : this(period: period, useNaN: false) { } + public MAPE_Series(TBars source) : this(source.Close, 0, false) { } + public MAPE_Series(TBars source, int period) : this(source.Close, period, false) { } + public MAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MAPE_Series(TSeries source) : this(source, 0, false) { } + public MAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _mape = 0; + for (int i = 0; i < _buffer.Count; i++) + { + _mape += (_buffer[i] != 0) ? Math.Abs(_buffer[i] - _sma) / Math.Abs(_buffer[i]) : double.PositiveInfinity; + } + _mape /= (_buffer.Count > 0) ? _buffer.Count : 1; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mape); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MAX_Series.cs b/archive/Calculations/_Updated/MAX_Series.cs new file mode 100644 index 00000000..17aa9c71 --- /dev/null +++ b/archive/Calculations/_Updated/MAX_Series.cs @@ -0,0 +1,77 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MAX - Maximum value in the given period in the series. + If period = 0 => period = full length of the series + + */ + +public class MAX_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MAX_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MAX({period})"; + } + public MAX_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MAX_Series() : this(period: 0, useNaN: false) { } + public MAX_Series(int period) : this(period: period, useNaN: false) { } + public MAX_Series(TBars source) : this(source.Close, 0, false) { } + public MAX_Series(TBars source, int period) : this(source.Close, period, false) { } + public MAX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MAX_Series(TSeries source) : this(source, 0, false) { } + public MAX_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _max = _buffer.Max(); + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MEDIAN_Series.cs b/archive/Calculations/_Updated/MEDIAN_Series.cs new file mode 100644 index 00000000..e431c979 --- /dev/null +++ b/archive/Calculations/_Updated/MEDIAN_Series.cs @@ -0,0 +1,95 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MED - Median value + Median of numbers is the middlemost value of the given set of numbers. + It separates the higher half and the lower half of a given data sample. + At least half of the observations are smaller than or equal to median + and at least half of the observations are greater than or equal to the median. + + If the number of values is odd, the middlemost observation of the sorted + list is the median of the given data. If the number of values is even, + median is the average of (n/2)th and [(n/2) + 1]th values of the sorted list. + + If period = 0 => period is max + +Sources: + https://corporatefinanceinstitute.com/resources/knowledge/other/median/ + https://en.wikipedia.org/wiki/Median + + */ + +public class MEDIAN_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MEDIAN_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MEDIAN({period})"; + } + public MEDIAN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MEDIAN_Series() : this(period: 0, useNaN: false) { } + public MEDIAN_Series(int period) : this(period: period, useNaN: false) { } + public MEDIAN_Series(TBars source) : this(source.Close, 0, false) { } + public MEDIAN_Series(TBars source, int period) : this(source.Close, period, false) { } + public MEDIAN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MEDIAN_Series(TSeries source) : this(source, 0, false) { } + public MEDIAN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + System.Collections.Generic.List _s = new(this._buffer); + _s.Sort(); + int _p1 = _s.Count / 2; + int _p2 = Math.Max(0, (_s.Count / 2) - 1); + double _med = (_s.Count % 2 != 0) ? _s[_p1] : (_s[_p1] + _s[_p2]) / 2; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _med); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MIDPOINT_Series.cs b/archive/Calculations/_Updated/MIDPOINT_Series.cs new file mode 100644 index 00000000..ba98b3b3 --- /dev/null +++ b/archive/Calculations/_Updated/MIDPOINT_Series.cs @@ -0,0 +1,81 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MIDPOINT: Midpoint value (max+min)/2 in the given period in the series. + If period = 0 => period = full length of the series + +Sources: + https://thefaqblog.com/what-is-the-midpoint-in-statistics/ + + */ + +public class MIDPOINT_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MIDPOINT_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MIDPOINT({period})"; + } + public MIDPOINT_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MIDPOINT_Series() : this(period: 0, useNaN: false) { } + public MIDPOINT_Series(int period) : this(period: period, useNaN: false) { } + public MIDPOINT_Series(TBars source) : this(source.Close, 0, false) { } + public MIDPOINT_Series(TBars source, int period) : this(source.Close, period, false) { } + public MIDPOINT_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MIDPOINT_Series(TSeries source) : this(source, 0, false) { } + public MIDPOINT_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _max = _buffer.Max(); + double _min = _buffer.Min(); + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : (_max + _min) * 0.5); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MIDPRICE_Series.cs b/archive/Calculations/_Updated/MIDPRICE_Series.cs new file mode 100644 index 00000000..851b23fd --- /dev/null +++ b/archive/Calculations/_Updated/MIDPRICE_Series.cs @@ -0,0 +1,74 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +MIDPRICE: Midpoint price (highhest high + lowest low)/2 in the given period in the series. + If period = 0 => period = full length of the series + + */ + +public class MIDPRICE_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private readonly System.Collections.Generic.List _bufferhi = new(); + private readonly System.Collections.Generic.List _bufferlo = new(); + + //core constructors + public MIDPRICE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MIDPRICE({period})"; + } + public MIDPRICE_Series(TBars source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public MIDPRICE_Series() : this(period: 2, useNaN: false) { } + public MIDPRICE_Series(int period) : this(period: period, useNaN: false) { } + public MIDPRICE_Series(TBars source) : this(source, period: 2, useNaN: false) { } + public MIDPRICE_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + BufferTrim(_bufferhi, TBar.h, _period, update); + BufferTrim(_bufferlo, TBar.l, _period, update); + double _mid = (_bufferhi.Max() + _bufferlo.Min()) * 0.5; + + var res = (TBar.t, Count < _period - 1 && _NaN ? double.NaN : _mid); + return base.Add(res, update); + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _bufferhi.Clear(); + _bufferlo.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MIN_Series.cs b/archive/Calculations/_Updated/MIN_Series.cs new file mode 100644 index 00000000..d3d7bb73 --- /dev/null +++ b/archive/Calculations/_Updated/MIN_Series.cs @@ -0,0 +1,77 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MIN - Minimum value in the given period in the series. + If period = 0 => period = full length of the series + + */ + +public class MIN_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MIN_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MAX({period})"; + } + public MIN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MIN_Series() : this(period: 0, useNaN: false) { } + public MIN_Series(int period) : this(period: period, useNaN: false) { } + public MIN_Series(TBars source) : this(source.Close, 0, false) { } + public MIN_Series(TBars source, int period) : this(source.Close, period, false) { } + public MIN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MIN_Series(TSeries source) : this(source, 0, false) { } + public MIN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _max = _buffer.Min(); + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/MSE_Series.cs b/archive/Calculations/_Updated/MSE_Series.cs new file mode 100644 index 00000000..34440185 --- /dev/null +++ b/archive/Calculations/_Updated/MSE_Series.cs @@ -0,0 +1,85 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +MSE: Mean Square Error + Defined as a Mean (Average) of the Square of the difference between actual and estimated values. + +Sources: + https://en.wikipedia.org/wiki/Mean_squared_error + + */ + +public class MSE_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public MSE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"MSE({period})"; + } + public MSE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public MSE_Series() : this(period: 0, useNaN: false) { } + public MSE_Series(int period) : this(period: period, useNaN: false) { } + public MSE_Series(TBars source) : this(source.Close, 0, false) { } + public MSE_Series(TBars source, int period) : this(source.Close, period, false) { } + public MSE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public MSE_Series(TSeries source) : this(source, 0, false) { } + public MSE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _mse = 0; + for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); } + _mse /= this._buffer.Count; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mse); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/OBV_Series.cs b/archive/Calculations/_Updated/OBV_Series.cs new file mode 100644 index 00000000..2025034e --- /dev/null +++ b/archive/Calculations/_Updated/OBV_Series.cs @@ -0,0 +1,106 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +OBV: On-Balance Volume + On-balance volume (OBV) is a technical trading momentum indicator that uses volume flow to predict + changes in stock price. Joseph Granville first developed the OBV metric in the 1963 book + Granville's New Key to Stock Market Profits. + + | +volume; if close > close[previous] + OBV = OBV[previous] + | 0; if close = close[previous] + | -volume; if close < close[previous] + +Sources: + https://www.investopedia.com/terms/o/onbalancevolume.asp + https://www.tradingview.com/wiki/On_Balance_Volume_(OBV) + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/on-balance-volume-obv/ + https://www.motivewave.com/studies/on_balance_volume.htm + +Note: + There is no consensus on what is the first OBV value in the series: + - TA-LIB uses the first volume: OBV[0] = volume[0] + - Skender stock library uses 0: OBV[0] = 0 + + */ + +public class OBV_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TBars _data; + private double _lastobv, _lastlastobv; + private double _lastclose, _lastlastclose; + + //core constructors + public OBV_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"OBV({period})"; + this._lastobv = this._lastlastobv = 0; + this._lastclose = this._lastlastclose = 0; + } + public OBV_Series(TBars source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(data: _data); + } + public OBV_Series() : this(period: 2, useNaN: false) { } + public OBV_Series(int period) : this(period: period, useNaN: false) { } + public OBV_Series(TBars source) : this(source, period: 2, useNaN: false) { } + public OBV_Series(TBars source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + + if (update) + { + this._lastobv = this._lastlastobv; + this._lastclose = this._lastlastclose; + } + + double _obv = this._lastobv; + if (TBar.c > this._lastclose) { _obv += TBar.v; } + if (TBar.c < this._lastclose) { _obv -= TBar.v; } + + this._lastlastobv = this._lastobv; + this._lastobv = _obv; + + this._lastlastclose = this._lastclose; + this._lastclose = TBar.c; + + var res = (TBar.t, (this.Count < this._period && this._NaN) ? double.NaN : _obv); + return base.Add(res, update); + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + this._lastobv = this._lastlastobv = 0; + this._lastclose = this._lastlastclose = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/RMA_Series.cs b/archive/Calculations/_Updated/RMA_Series.cs new file mode 100644 index 00000000..a1b94259 --- /dev/null +++ b/archive/Calculations/_Updated/RMA_Series.cs @@ -0,0 +1,133 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +RMA: wildeR Moving Average + J. Welles Wilder introduced RMA as an alternative to EMA. RMA's weight (k) is + set as 1/period, giving less weight to the new data compared to EMA. + +Sources: + https://archive.org/details/newconceptsintec00wild/page/23/mode/2up + https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing + https://www.incrediblecharts.com/indicators/wilder_moving_average.php + +Issues: + Pandas-TA library calculates RMA using straight Exponential Weighted Mean: + pandas.ewm().mean() and returns incorrect first (period) of bars compared to + published formula. This implementation passess the validation test in Wilder's book. + + */ + +public class RMA_Series : TSeries +{ + private double _k; + private double _lastrma, _oldrma; + private double _sum, _oldsum; + private readonly bool _useSMA; + private int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructor + public RMA_Series(int period, bool useNaN, bool useSMA) + { + _period = period; + _NaN = useNaN; + _useSMA = useSMA; + Name = $"RMA({period})"; + _k = 1.0 / (double)(this._period); + _len = 0; + _sum = _oldsum = _lastrma = _oldrma = 0; + } + //generic constructors (source) + + public RMA_Series() : this(0, false, true) { } + public RMA_Series(int period) : this(period, false, true) { } + public RMA_Series(TBars source) : this(source.Close, 0, false) { } + public RMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public RMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public RMA_Series(TSeries source, int period) : this(source, period, false, true) { } + public RMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } + public RMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (update) + { + _lastrma = _oldrma; + _sum = _oldsum; + } + else + { + _oldrma = _lastrma; + _oldsum = _sum; + _len++; + } + + double _rma = 0; + if (_period == 0) + { + _k = 1.0 / (double)(this._len); + } + + if (Count == 0) + { + _rma = _sum = TValue.v; + + } + else if (_len <= _period && _useSMA && _period != 0) + { + _sum += TValue.v; + if (_period != 0 && _len > _period) + { + _sum -= _data[Count - _period - (update ? 1 : 0)].v; + } + _rma = _sum / Math.Min(_len, _period); + } + else + { + _rma = _k * (TValue.v - _lastrma) + _lastrma; + } + + _lastrma = double.IsNaN(_rma) ? _lastrma : _rma; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rma); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _sum = _oldsum = _lastrma = _oldrma = 0; + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/RSI_Series.cs b/archive/Calculations/_Updated/RSI_Series.cs new file mode 100644 index 00000000..6a037b28 --- /dev/null +++ b/archive/Calculations/_Updated/RSI_Series.cs @@ -0,0 +1,134 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +RSI: Relative Strength Index + Created by J. Welles Wilder, the Relative Strength Index measures strength + of the winning/losing streak over N lookback periods on a scale of 0 to 100, + to depict overbought and oversold conditions. + +Sources: + https://www.investopedia.com/terms/r/rsi.asp + + */ + +public class RSI_Series : TSeries +{ + private readonly System.Collections.Generic.List _gain = new(); + private readonly System.Collections.Generic.List _loss = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private double _avgGain, _avgLoss, _lastValue; + private double _avgGain_o, _avgLoss_o, _lastValue_o; + private int i; + + //core constructors + public RSI_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"RSI({period})"; + i = 0; + } + public RSI_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public RSI_Series() : this(period: 0, useNaN: false) { } + public RSI_Series(int period) : this(period: period, useNaN: false) { } + public RSI_Series(TBars source) : this(source.Close, 0, false) { } + public RSI_Series(TBars source, int period) : this(source.Close, period, false) { } + public RSI_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public RSI_Series(TSeries source) : this(source, 0, false) { } + public RSI_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + + double _rsi = 0; + if (update) + { + _lastValue = _lastValue_o; + _avgGain = _avgGain_o; + _avgLoss = _avgLoss_o; + } + else + { + _lastValue_o = _lastValue; + _avgGain_o = _avgGain; + _avgLoss_o = _avgLoss; + } + + if (i == 0) { _lastValue = TValue.v; } + + double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0; + BufferTrim(_gain, _gainval, _period, update); + double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0; + BufferTrim(_loss, _lossval, _period, update); + _lastValue = TValue.v; + + // calculate RSI + if (i > _period && _period != 0) + { + _avgGain = ((_avgGain * (_period - 1)) + _gain[^1]) / _period; + _avgLoss = ((_avgLoss * (_period - 1)) + _loss[^1]) / _period; + if (_avgLoss > 0) + { + double rs = _avgGain / _avgLoss; + _rsi = 100 - (100 / (1 + rs)); + } + else { _rsi = 100; } + } + // initialize average gain + else + { + double _sumGain = 0; + for (int p = 0; p < _gain.Count; p++) { _sumGain += _gain[p]; } + double _sumLoss = 0; + for (int p = 0; p < _loss.Count; p++) { _sumLoss += _loss[p]; } + + _avgGain = _sumGain / _gain.Count; + _avgLoss = _sumLoss / _loss.Count; + + _rsi = (_avgLoss > 0) ? 100 - (100 / (1 + (_avgGain / _avgLoss))) : 100; + } + if (!update) { i++; } + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rsi); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + i = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SDEV_Series.cs b/archive/Calculations/_Updated/SDEV_Series.cs new file mode 100644 index 00000000..88fe0ce1 --- /dev/null +++ b/archive/Calculations/_Updated/SDEV_Series.cs @@ -0,0 +1,91 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +SDEV: Population Standard Deviation + Population Standard Deviation is the square root of the biased variance, also knons as + Uncorrected Sample Standard Deviation + +Sources: + https://en.wikipedia.org/wiki/Standard_deviation#Uncorrected_sample_standard_deviation + +Remark: + SDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation. + For unbiased version that uses Bessel's correction, use SDEV instead. + + */ + +public class SDEV_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public SDEV_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"SDEV({period})"; + } + public SDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SDEV_Series() : this(period: 0, useNaN: false) { } + public SDEV_Series(int period) : this(period: period, useNaN: false) { } + public SDEV_Series(TBars source) : this(source.Close, 0, false) { } + public SDEV_Series(TBars source, int period) : this(source.Close, period, false) { } + public SDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SDEV_Series(TSeries source) : this(source, 0, false) { } + public SDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _var = 0; + for (int i = 0; i < _buffer.Count; i++) { _var += (_buffer[i] - _sma) * (_buffer[i] - _sma); } + _var /= this._buffer.Count; + double _sdev = Math.Sqrt(_var); + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sdev); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SLOPE_Series.cs b/archive/Calculations/_Updated/SLOPE_Series.cs new file mode 100644 index 00000000..c47b8650 --- /dev/null +++ b/archive/Calculations/_Updated/SLOPE_Series.cs @@ -0,0 +1,130 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +SLOPE: Slope of linear regression (using Least Square Method) + Linear Regression provides a slope of a straight line that is the best approximation of the given set of data. + The method of least squares is a standard approach in linear regression analysis to approximate the solution + by minimizing the sum of the squares of the residuals made in the results of each individual equation. + +Additional outputs provided by LINREG: + .Intercept - y-intercept point of the best fit line + .RSquared - R-Squared (R²), Coefficient of Determination + .StdDev - Standard Deviation of data over given periods + + y = Slope * x + Intercept + +Sources: + https://en.wikipedia.org/wiki/Least_squares + + */ + +public class SLOPE_Series : TSeries +{ + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly TSeries p_Intercept = new(); + private readonly TSeries p_RSquared = new(); + private readonly TSeries p_StdDev = new(); + private readonly System.Collections.Generic.List _buffer = new(); + public TSeries Intercept => p_Intercept; + public TSeries RSquared => p_RSquared; + public TSeries StdDev => p_StdDev; + //core constructors + public SLOPE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"SLOPE({period})"; + } + public SLOPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SLOPE_Series() : this(period: 0, useNaN: false) { } + public SLOPE_Series(int period) : this(period: period, useNaN: false) { } + public SLOPE_Series(TBars source) : this(source.Close, 0, false) { } + public SLOPE_Series(TBars source, int period) : this(source.Close, period, false) { } + public SLOPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SLOPE_Series(TSeries source) : this(source, 0, false) { } + public SLOPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + int _len = this._buffer.Count; + + // get averages for period + double sumX = 0; + double sumY = 0; + + for (int p = 0; p < _len; p++) + { + sumX += this.Count - _len + 2 + p; + sumY += _buffer[p]; + } + double avgX = sumX / _len; + double avgY = sumY / _len; + + // least squares method + double sumSqX = 0; + double sumSqY = 0; + double sumSqXY = 0; + + for (int p = 0; p < _len; p++) + { + double devX = this.Count - _len + 2 + p - avgX; + double devY = _buffer[p] - avgY; + + sumSqX += devX * devX; + sumSqY += devY * devY; + sumSqXY += devX * devY; + } + + double _slope = sumSqXY / sumSqX; + double _intercept = avgY - (_slope * avgX); + + // calculate Standard Deviation and R-Squared + double stdDevX = Math.Sqrt(sumSqX / _len); + double stdDevY = Math.Sqrt(sumSqY / _len); + double _StdDev = stdDevY; + + double arrr = (stdDevX * stdDevY != 0) ? sumSqXY / (stdDevX * stdDevY) / _len : 0; + double _RSquared = arrr * arrr; + + var ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _intercept); + p_Intercept.Add(ret, update); + + ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _StdDev); + p_StdDev.Add(ret, update); + + ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _RSquared); + p_RSquared.Add(ret, update); + + ret = (TValue.t, this.Count < this._period - 1 && this._NaN ? double.NaN : _slope); + return base.Add(ret, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SMAPE_Series.cs b/archive/Calculations/_Updated/SMAPE_Series.cs new file mode 100644 index 00000000..a2e65e05 --- /dev/null +++ b/archive/Calculations/_Updated/SMAPE_Series.cs @@ -0,0 +1,84 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +SMAPE: Symmetric Mean Absolute Percentage Error + Measures the size of the error in percentage terms + +Sources: + https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error + + */ + +public class SMAPE_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public SMAPE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"SMAPE({period})"; + } + public SMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SMAPE_Series() : this(period: 0, useNaN: false) { } + public SMAPE_Series(int period) : this(period: period, useNaN: false) { } + public SMAPE_Series(TBars source) : this(source.Close, 0, false) { } + public SMAPE_Series(TBars source, int period) : this(source.Close, period, false) { } + public SMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SMAPE_Series(TSeries source) : this(source, 0, false) { } + public SMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + double _smape = 0; + for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); } + _smape /= this._buffer.Count; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smape); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SMA_Series.cs b/archive/Calculations/_Updated/SMA_Series.cs new file mode 100644 index 00000000..e4c7b9e6 --- /dev/null +++ b/archive/Calculations/_Updated/SMA_Series.cs @@ -0,0 +1,112 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +SMA: Simple Moving Average + The weights are equally distributed across the period, resulting in a mean() of + the data within the period + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/ + https://stats.stackexchange.com/a/24739 + +Remark: + This calc doesn't use LINQ or SUM() or any of (slow) iterative methods. It is not as fast as TA-LIB + implementation, but it does allow incremental additions of inputs and real-time calculations of SMA() + + */ +public class SMA_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + + private double _sum, _oldsum; + private readonly int _period; + private readonly TSeries _data; + protected readonly bool _NaN; + + //core constructor + public SMA_Series(int period, bool useNaN) + { + _period = Math.Max(0, period); + _NaN = useNaN; + Name = $"SMA({period})"; + _sum = _oldsum = 0; + } + public SMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SMA_Series() : this(0, false) { } + public SMA_Series(int period) : this(period, false) { } + public SMA_Series(TBars source) : this(source.Close, 0, false) { } + public SMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public SMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SMA_Series(TSeries source) : this(source, 0, false) { } + public SMA_Series(TSeries source, int period) : this(source, period, false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return (TValue.t, double.NaN); + } + else + { + if (update && _buffer.Count > 0) + { + _sum -= _buffer[^1]; + _buffer[^1] = TValue.v; + _oldsum = _sum; + } + else + { + _buffer.Add(TValue.v); + _oldsum = _sum; + } + + _sum += TValue.v; + if (_period != 0 && _buffer.Count > _period) + { + _sum -= _buffer[0]; + _buffer.RemoveAt(0); + } + } + + double _div = _period == 0 ? _buffer.Count : Math.Min(_buffer.Count, _period); + var _sma = _sum / _div; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sma); + return base.Add(res, update); + } + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + + //reset calculation + public override void Reset() + { + _sum = _oldsum = 0; + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SMMA_Series.cs b/archive/Calculations/_Updated/SMMA_Series.cs new file mode 100644 index 00000000..cfba2a57 --- /dev/null +++ b/archive/Calculations/_Updated/SMMA_Series.cs @@ -0,0 +1,105 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Linq; + +/* +SMMA: Smoothed Moving Average + The Smoothed Moving Average (SMMA) is a combination of a SMA and an EMA. It gives the recent prices + an equal weighting as the historic prices as it takes all available price data into account. + The main advantage of a smoothed moving average is that it removes short-term fluctuations. + + SMMA(i) = (SMMA-1*(N-1) + CLOSE (i)) / N + +Sources: + https://blog.earn2trade.com/smoothed-moving-average + https://guide.traderevolution.com/traderevolution/mobile-applications/phone/android/technical-indicators/moving-averages/smma-smoothed-moving-average + https://www.chartmill.com/documentation/technical-analysis-indicators/217-MOVING-AVERAGES-%7C-The-Smoothed-Moving-Average-%28SMMA%29 + + */ + +public class SMMA_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private double _lastsmma, _lastlastsmma; + + //core constructors + public SMMA_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"SMMA({period})"; + } + public SMMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SMMA_Series() : this(period: 0, useNaN: false) { } + public SMMA_Series(int period) : this(period: period, useNaN: false) { } + public SMMA_Series(TBars source) : this(source.Close, 0, false) { } + public SMMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public SMMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SMMA_Series(TSeries source) : this(source, 0, false) { } + public SMMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, double.NaN), update); + } + + double _smma = 0; + if (update) { this._lastsmma = this._lastlastsmma; } + + if (this.Count < this._period) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + _smma = _buffer.Average(); + } + else + { + _smma = ((_lastsmma * (_period - 1)) + TValue.v) / _period; + } + + this._lastlastsmma = this._lastsmma; + this._lastsmma = _smma; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smma); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + this._lastsmma = this._lastlastsmma = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SSDEV_Series.cs b/archive/Calculations/_Updated/SSDEV_Series.cs new file mode 100644 index 00000000..ae2a10de --- /dev/null +++ b/archive/Calculations/_Updated/SSDEV_Series.cs @@ -0,0 +1,91 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +SSDEV: (Corrected) Sample Standard Deviation + Sample Standard Deviaton uses Bessel's correction to correct the bias in the variance. + +Sources: + https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation + Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction + +Remark: + SSDEV (Sample Standard Deviation) is also known as a unbiased/corrected Standard Deviation. + For a population/biased/uncorrected Standard Deviation, use PSDEV instead + + */ + +public class SSDEV_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public SSDEV_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"SSDEV({period})"; + } + public SSDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SSDEV_Series() : this(period: 0, useNaN: false) { } + public SSDEV_Series(int period) : this(period: period, useNaN: false) { } + public SSDEV_Series(TBars source) : this(source.Close, 0, false) { } + public SSDEV_Series(TBars source, int period) : this(source.Close, period, false) { } + public SSDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SSDEV_Series(TSeries source) : this(source, 0, false) { } + public SSDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _svar = 0; + for (int i = 0; i < this._buffer.Count; i++) { _svar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } + _svar /= (_buffer.Count > 1) ? _buffer.Count - 1 : 1; // Bessel's correction + double _ssdev = Math.Sqrt(_svar); + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ssdev); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/SVAR_Series.cs b/archive/Calculations/_Updated/SVAR_Series.cs new file mode 100644 index 00000000..3438c9d5 --- /dev/null +++ b/archive/Calculations/_Updated/SVAR_Series.cs @@ -0,0 +1,90 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +VAR: Population Variance + Population variance without Bessel's correction + +Sources: + https://en.wikipedia.org/wiki/Variance + Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction + +Remark: + VAR (Population Variance) is also known as a biased Sample Variance. For unbiased + sample variance use SVAR instead. + + */ + +public class SVAR_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public SVAR_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"SVAR({period})"; + } + public SVAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public SVAR_Series() : this(period: 0, useNaN: false) { } + public SVAR_Series(int period) : this(period: period, useNaN: false) { } + public SVAR_Series(TBars source) : this(source.Close, 0, false) { } + public SVAR_Series(TBars source, int period) : this(source.Close, period, false) { } + public SVAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public SVAR_Series(TSeries source) : this(source, 0, false) { } + public SVAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _svar = 0; + for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); } + _svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _svar); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/T3_Series.cs b/archive/Calculations/_Updated/T3_Series.cs new file mode 100644 index 00000000..b203f818 --- /dev/null +++ b/archive/Calculations/_Updated/T3_Series.cs @@ -0,0 +1,173 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Numerics; + +/* +T3: Tillson T3 Moving Average + Tim Tillson described it in "Technical Analysis of Stocks and Commodities", January 1998 in the + article "Better Moving Averages". Tillson’s moving average becomes a popular indicator of + technical analysis as it gets less lag with the price chart and its curve is considerably smoother. + +Sources: + https://technicalindicators.net/indicators-technical-analysis/150-t3-moving-average + http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/ + */ + +public class T3_Series : TSeries +{ + private readonly double _k, _k1m, _c1, _c2, _c3, _c4; + private readonly System.Collections.Generic.List _buffer1 = new(); + private readonly System.Collections.Generic.List _buffer2 = new(); + private readonly System.Collections.Generic.List _buffer3 = new(); + private readonly System.Collections.Generic.List _buffer4 = new(); + private readonly System.Collections.Generic.List _buffer5 = new(); + private readonly System.Collections.Generic.List _buffer6 = new(); + private readonly bool _useSMA; + private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6; + private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6; + protected int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) + { + _period = period; + _len = 0; + _NaN = useNaN; + Name = $"T3({period})"; + _useSMA = useSMA; + double _a = vfactor; //0.7; //0.618 + _c1 = -_a * _a * _a; + _c2 = 3 * _a * _a + 3 * _a * _a * _a; + _c3 = -6 * _a * _a - 3 * _a - 3 * _a * _a * _a; + _c4 = 1 + 3 * _a + _a * _a * _a + 3 * _a * _a; + + _k = 2.0 / (_period + 1); + _k1m = 1.0 - _k; + _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0; + } + public T3_Series(TSeries source, int period, double vfactor, bool useSMA, bool useNaN) : this(period, vfactor, useSMA, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public T3_Series() : this(period: 0, vfactor: 0.7, useSMA: true, useNaN: false) { } + public T3_Series(int period) : this(period: period, vfactor: 0.7, useSMA: true, useNaN: false) { } + public T3_Series(TBars source) : this(source.Close, 0, vfactor: 0.7, useSMA: true, useNaN: false) { } + public T3_Series(TBars source, int period) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: false) { } + public T3_Series(TBars source, int period, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { } + public T3_Series(TBars source, int period, double vfactor, bool useNaN) : this(source.Close, period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { } + public T3_Series(TBars source, int period, bool useSMA, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: useSMA, useNaN: useNaN) { } + public T3_Series(TSeries source) : this(source, 0, vfactor: 0.7, useSMA: true, useNaN: false) { } + public T3_Series(TSeries source, int period) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: false) { } + public T3_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { } + public T3_Series(TSeries source, int period, double vfactor) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: false) { } + public T3_Series(TSeries source, int period, double vfactor, bool useNaN) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6; + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + + if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; _lastema4 = _llastema4; _lastema5 = _llastema5; _lastema6 = _llastema6; } + else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _llastema4 = _lastema4; _llastema5 = _lastema5; _llastema6 = _lastema6; } + + if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; } + + + if ((_len < _period) && _useSMA) + { + BufferTrim(_buffer1, TValue.v, _period, update); + _ema1 = 0; + for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; } + _ema1 /= _buffer1.Count; + + BufferTrim(_buffer2, _ema1, _period, update); + _ema2 = 0; + for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; } + _ema2 /= _buffer2.Count; + + BufferTrim(_buffer3, _ema2, _period, update); + _ema3 = 0; + for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; } + _ema3 /= _buffer3.Count; + + BufferTrim(_buffer4, _ema3, _period, update); + _ema4 = 0; + for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; } + _ema4 /= _buffer4.Count; + + BufferTrim(_buffer5, _ema4, _period, update); + _ema5 = 0; + for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; } + _ema5 /= _buffer5.Count; + + BufferTrim(_buffer6, _ema5, _period, update); + _ema6 = 0; + for (int i = 0; i < _buffer6.Count; i++) { _ema6 += _buffer6[i]; } + _ema6 /= _buffer6.Count; + } + else + { + _ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m); + _ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m); + _ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m); + _ema4 = (_ema3 * this._k) + (this._lastema4 * this._k1m); + _ema5 = (_ema4 * this._k) + (this._lastema5 * this._k1m); + _ema6 = (_ema5 * this._k) + (this._lastema6 * this._k1m); + } + _len++; + _lastema1 = _ema1; + _lastema2 = _ema2; + _lastema3 = _ema3; + _lastema4 = _ema4; + _lastema5 = _ema5; + _lastema6 = _ema6; + + double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _T3); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0; + _buffer1.Clear(); + _buffer2.Clear(); + _buffer3.Clear(); + _buffer4.Clear(); + _buffer5.Clear(); + _buffer6.Clear(); + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TBars.cs b/archive/Calculations/_Updated/TBars.cs new file mode 100644 index 00000000..2d72df30 --- /dev/null +++ b/archive/Calculations/_Updated/TBars.cs @@ -0,0 +1,153 @@ +namespace QuanTAlib; +using System; + +/* +TBars class - includes all series for common data used in indicators and other calculations. + Has a bit limited overloading and casting (compared to TSeries) + Includes Select(int) method to simplify choosing the most optimal data source for indicators + Includes the most basic pricing calcs: HL2, OC2, OHL3, HLC3, OHLC4, HLCC4 + (it is 'cheaper' to calculate them once during data capture than each time during data analysis) + + */ + +public class TBars : System.Collections.Generic.List<(DateTime t, double o, double h, double l, double c, double v)> +{ + public string Name { get; set; } + private readonly TSeries _open = new("open"); + private readonly TSeries _high = new("high"); + private readonly TSeries _low = new("low"); + private readonly TSeries _close = new("close"); + private readonly TSeries _volume = new("volume"); + private readonly TSeries _hl2 = new("HL2"); + private readonly TSeries _oc2 = new("OC2"); + private readonly TSeries _ohl3 = new("OHL3"); + private readonly TSeries _hlc3 = new("HLC3"); + private readonly TSeries _ohlc4 = new("OHLC4"); + private readonly TSeries _hlcc4 = new("HLCC4"); + + public TSeries Open => this._open; + public TSeries High => this._high; + public TSeries Low => this._low; + public TSeries Close => this._close; + public TSeries Volume => this._volume; + public TSeries HL2 => this._hl2; + public TSeries OC2 => this._oc2; + public TSeries OHL3 => this._ohl3; + public TSeries HLC3 => this._hlc3; + public TSeries OHLC4 => this._ohlc4; + public TSeries HLCC4 => this._hlcc4; + + public TBars() { } + + public TBars(string Name) + { + this.Name = Name; + } + + public (DateTime t, double o, double h, double l, double c, double v) Last => this[^1]; + public TBars Tail(int count = 10) + { + TBars outBars = new(); + if (count > this.Count) { count = this.Count; } + for (int i = this.Count - count; i < this.Count; i++) { outBars.Add(this[i]); } + return outBars; + } + public TSeries Select(int source) + { + return source switch + { + 0 => _open, + 1 => _high, + 2 => _low, + 3 => _close, + 4 => _hl2, + 5 => _oc2, + 6 => _ohl3, + 7 => _hlc3, + 8 => _ohlc4, + _ => _hlcc4, + }; + } + public static string SelectStr(int source) + { + return source switch + { + 0 => "Open", + 1 => "High", + 2 => "Low", + 3 => "Close", + 4 => "HL2", + 5 => "OC2", + 6 => "OHL3", + 7 => "HLC3", + 8 => "OHLC4", + _ => "HLCC4", + }; + } + + public virtual (DateTime t, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) => + Add((t: (this.Count == 0) ? DateTime.Today : this[^1].t.AddDays(1), p.o, p.h, p.l, p.c, p.v), update); + + public virtual (DateTime t, double v) Add(double o, double h, double l, double c, double v, bool update = false) => + Add((o, h, l, c, v), update); + + public virtual (DateTime t, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) => + this.Add((t, o, h, l, c, v), update); + + public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + if (update) { this[^1] = TBar; } else { base.Add(TBar); } + + _open.Add((TBar.t, TBar.o), update); + _high.Add((TBar.t, TBar.h), update); + _low.Add((TBar.t, TBar.l), update); + _close.Add((TBar.t, TBar.c), update); + _volume.Add((TBar.t, TBar.v), update); + _hl2.Add((TBar.t, (TBar.h + TBar.l) * 0.5), update); + _oc2.Add((TBar.t, (TBar.o + TBar.c) * 0.5), update); + _ohl3.Add((TBar.t, (TBar.o + TBar.h + TBar.l) * 0.333333333333333), update); + _hlc3.Add((TBar.t, (TBar.h + TBar.l + TBar.c) * 0.333333333333333), update); + _ohlc4.Add((TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25), update); + _hlcc4.Add((TBar.t, (TBar.h + TBar.l + TBar.c + TBar.c) * 0.25), update); + + this.OnEvent(update); + return (TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25); + } + + public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); + public event NewDataEventHandler Pub; + protected virtual void OnEvent(bool update = false) + { + if (Pub != null && Pub.Target != this) + { + Pub(this, new TSeriesEventArgs { update = update }); + } + } + + public void Sub(object source, TSeriesEventArgs e) + { + TBars ss = (TBars)source; if (ss.Count > 1) + { + for (int i = 0; i < ss.Count; i++) { this.Add(ss[i]); } + } + else + { + this.Add(ss[^1], e.update); + } + } + + /// common helpers + public static void BufferTrim(System.Collections.Generic.List buffer, double value, int period, bool update) + { + if (!update) + { + buffer.Add(value); + if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); } + return; + } + buffer[^1] = value; + } + public virtual void Reset() + { + } +} diff --git a/archive/Calculations/_Updated/TEMA_Series.cs b/archive/Calculations/_Updated/TEMA_Series.cs new file mode 100644 index 00000000..f1720fad --- /dev/null +++ b/archive/Calculations/_Updated/TEMA_Series.cs @@ -0,0 +1,134 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +TEMA: Triple Exponential Moving Average + TEMA uses EMA(EMA(EMA())) to calculate less laggy Exponential moving average. + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/ + +Remark: + ema1 = EMA(close, length) + ema2 = EMA(ema1, length) + ema3 = EMA(ema2, length) + TEMA = 3 * (ema1 - ema2) + ema3 + + */ + +public class TEMA_Series : TSeries +{ + private double _k; + private double _sum, _oldsum; + private double _lastema1, _oldema1, _lastema2, _oldema2, _lastema3, _oldema3; + private int _len; + private readonly bool _useSMA; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructor + public TEMA_Series(int period, bool useNaN, bool useSMA) + { + _period = period; + _NaN = useNaN; + _useSMA = useSMA; + Name = $"TEMA({period})"; + _k = 2.0 / (_period + 1); + _len = 0; + _sum = _oldsum = _lastema1 = _lastema2 = _lastema3 = 0; + } + public TEMA_Series() : this(0, false, true) { } + public TEMA_Series(int period) : this(period, false, true) { } + public TEMA_Series(TBars source) : this(source.Close, 0, false) { } + public TEMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public TEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public TEMA_Series(TSeries source, int period) : this(source, period, false, true) { } + public TEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } + public TEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (update) + { + _lastema1 = _oldema1; + _lastema2 = _oldema2; + _lastema3 = _oldema3; + _sum = _oldsum; + } + else + { + _oldema1 = _lastema1; + _oldema2 = _lastema2; + _oldema3 = _lastema3; + _oldsum = _sum; + _len++; + } + + if (_period == 0) { _k = 2.0 / (_len + 1); } + + double _ema1, _ema2, _ema3, _tema; + if (this.Count == 0) + { + _ema1 = _ema2 = _ema3 = _sum = TValue.v; + } + else if (_len <= _period && _useSMA && _period != 0) + { + _sum += TValue.v; + _ema1 = _sum / Math.Min(_len, _period); + _ema2 = _ema1; + _ema3 = _ema2; + } + else + { + _ema1 = (TValue.v - _lastema1) * _k + _lastema1; + _ema2 = (_ema1 - _lastema2) * _k + _lastema2; + _ema3 = (_ema2 - _lastema3) * _k + _lastema3; + } + + _tema = (3 * (_ema1 - _ema2)) + _ema3; + + _lastema1 = Double.IsNaN(_ema1) ? _lastema1 : _ema1; + _lastema2 = Double.IsNaN(_ema2) ? _lastema2 : _ema2; + _lastema3 = Double.IsNaN(_ema3) ? _lastema3 : _ema3; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _tema); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _sum = _oldsum = _lastema1 = _lastema2 = 0; + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TRIMA_Series.cs b/archive/Calculations/_Updated/TRIMA_Series.cs new file mode 100644 index 00000000..3c972143 --- /dev/null +++ b/archive/Calculations/_Updated/TRIMA_Series.cs @@ -0,0 +1,94 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +TRIMA: Triangular Moving Average + A weighted moving average where the shape of the weights are triangular and the greatest + weight is in the middle of the period, + +Sources: + https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/ + +Remark: + trima = sma(sma(signal, n/2), n/2) + + */ + +public class TRIMA_Series : TSeries +{ + private readonly int _p1a, _p1b; + private readonly SMA_Series sma, trima; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public TRIMA_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"xMA({period})"; + _p1a = (int)Math.Floor((period * 0.5) + 1); + _p1b = (int)Math.Ceiling(0.5 * period); + sma = new(_p1a); + trima = new(_p1b); + + } + public TRIMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public TRIMA_Series() : this(period: 0, useNaN: false) { } + public TRIMA_Series(int period) : this(period: period, useNaN: false) { } + public TRIMA_Series(TBars source) : this(source.Close, 0, false) { } + public TRIMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public TRIMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public TRIMA_Series(TSeries source) : this(source, 0, false) { } + public TRIMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + + var _sma = sma.Add(TValue, update); + var _trima = trima.Add(_sma, update); + + var res = (_trima.t, Count < _period - 1 && _NaN ? double.NaN : _trima.v); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + sma.Reset(); + trima.Reset(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TRIX_Series.cs b/archive/Calculations/_Updated/TRIX_Series.cs new file mode 100644 index 00000000..490eeccb --- /dev/null +++ b/archive/Calculations/_Updated/TRIX_Series.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +TRIX: Triple Exponential Average Oscillator + Developed by Jack Hutson in the early 1980s, the triple exponential average (TRIX) + has become a popular technical analysis tool to aid chartists in spotting diversions + and directional cues in stock trading patterns. + +Sources: + https://www.investopedia.com/terms/t/trix.asp + + */ + +public class TRIX_Series : TSeries +{ + private readonly double _k; + private readonly System.Collections.Generic.List _buffer1 = new(); + private readonly System.Collections.Generic.List _buffer2 = new(); + private readonly System.Collections.Generic.List _buffer3 = new(); + private double _lastema1, _lastema2, _lastema3; + private double _llastema1, _llastema2, _llastema3; + private int _len; + private readonly bool _useSMA; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + + public TRIX_Series(int period, bool useNaN, bool useSMA) + { + _period = period; + _NaN = useNaN; + _useSMA = useSMA; + Name = $"TRIX({period})"; + _k = 2.0 / (_period + 1); + _len = 0; + _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0; + } + public TRIX_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public TRIX_Series() : this(0, false, true) { } + public TRIX_Series(int period) : this(period, false, true) { } + public TRIX_Series(TBars source) : this(source.Close, 0, false) { } + public TRIX_Series(TBars source, int period) : this(source.Close, period, false) { } + public TRIX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public TRIX_Series(TSeries source, int period) : this(source, period, false, true) { } + public TRIX_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } + + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (double.IsNaN(TValue.v)) + { + return base.Add((TValue.t, Double.NaN), update); + } + if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; } + if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; } + else + { + _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _len++; + } + + double _ema1, _ema2, _ema3; + if ((this.Count < _period) && _useSMA) + { + BufferTrim(_buffer1, TValue.v, _period, update); + _ema1 = 0; + for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; } + _ema1 /= _buffer1.Count; + + BufferTrim(_buffer2, _ema1, _period, update); + _ema2 = 0; + for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; } + _ema2 /= _buffer2.Count; + + BufferTrim(_buffer3, _ema2, _period, update); + _ema3 = 0; + for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; } + _ema3 /= _buffer3.Count; + } + else + { + _ema1 = (TValue.v - _lastema1) * _k + _lastema1; + _ema2 = (_ema1 - _lastema2) * _k + _lastema2; + _ema3 = (_ema2 - _lastema3) * _k + _lastema3; + } + double _trix = 100 * (_ema3 - _lastema3) / _lastema3; + _lastema1 = _ema1; + _lastema2 = _ema2; + _lastema3 = _ema3; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _trix); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _len = 0; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TR_Series.cs b/archive/Calculations/_Updated/TR_Series.cs new file mode 100644 index 00000000..49d942ce --- /dev/null +++ b/archive/Calculations/_Updated/TR_Series.cs @@ -0,0 +1,91 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +TR: True Range + True Range was introduced by J. Welles Wilder in his book New Concepts in Technical Trading Systems. + It measures the daily range plus any gap from the closing price of the preceding day. + +Calculation: + d1 = ABS(High - Low) + d2 = ABS(High - Previous close) + d3 = ABS(Previous close - Low) + TR = MAX(d1,d2,d3) + +Sources: + https://www.macroption.com/true-range/ + + */ + +public class TR_Series : TSeries +{ + protected readonly TBars _data; + private double _cm1, _cm1_o; + + //core constructors + public TR_Series() + { + Name = $"TR()"; + _cm1 = _cm1_o = double.NaN; + } + public TR_Series(TBars source) + { + _data = source; + Name = $"TR({(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _cm1 = _cm1_o = double.NaN; + _data.Pub += Sub; + Add(data: _data); + } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + + if (update) + { + _cm1 = _cm1_o; + } + else + { + _cm1_o = _cm1; + } + + if (_cm1 is double.NaN) + { + _cm1 = TBar.c; + } + + double d1 = Math.Abs(TBar.h - TBar.l); + double d2 = Math.Abs(_cm1 - TBar.h); + double d3 = Math.Abs(_cm1 - TBar.l); + _cm1 = TBar.c; + var ret = (TBar.t, Math.Max(d1, Math.Max(d2, d3))); + return base.Add(ret, update); + + } + + public new void Add(TBars data) + { + foreach (var item in data) { Add(item, false); } + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TBar: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TBar: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TBar: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _cm1 = _cm1_o = double.NaN; + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/TSeries.cs b/archive/Calculations/_Updated/TSeries.cs new file mode 100644 index 00000000..4763e6c3 --- /dev/null +++ b/archive/Calculations/_Updated/TSeries.cs @@ -0,0 +1,137 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data; +using System.Linq; + +/* +TSeries is the cornerstone of all QuanTAlib classes. + TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads + and other helpers that simplify usage of library. + Think of TSeries as an equivalent of Numpy array. + + - includes Length property (to mimic array's method) + - includes publishing and subscribing methods that attach to events + + */ +public class TSeriesEventArgs : EventArgs +{ + public bool update { get; set; } +} + +public class TSeries : List<(DateTime t, double v)> +{ + private readonly (DateTime t, double v) Default = (DateTime.MinValue, double.NaN); + public IEnumerable t => this.Select(item => item.t); + public IEnumerable v => this.Select(item => item.v); + public (DateTime t, double v) Last => Count > 0 ? this[^1] : Default; + + public int Length => Count; + public string Name { get; set; } + public int Keep = 0; + + public TSeries() + { + this.Name = "data"; + } + + public TSeries(string Name) + { + this.Name = Name; + } + + public virtual (DateTime t, double v) Add(double v, bool update = false) + { + return Add((t: Count == 0 ? DateTime.Today : this[^1].t.AddDays(1), v), update); + } + + public virtual (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + if (update) + { + this[^1] = TValue; + } + else + { + base.Add(TValue); + } + + OnEvent(update); + return TValue; + } + + public virtual (DateTime t, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) + { + if (update) + { + this[this.Count - 1] = (TBar.t, TBar.c); + } + else + { + base.Add((TBar.t, TBar.c)); + } + + OnEvent(update); + return (TBar.t, TBar.c); + } + + public virtual (DateTime t, double v) Add(TSeries data) + { + foreach (var item in data) { Add(item); } + return data.Last; + } + + public virtual (DateTime t, double v) Add(TBars data) + { + foreach (var item in data) { Add(item.c, false); } + return (data.Last.t, data.Last.c); + } + + public void Sub(object source, TSeriesEventArgs e) + { + var data = (TSeries)source; + if (data == null) { return; } + foreach (var item in data) { Add(item); } + } + + public delegate void NewEventHandler(object source, TSeriesEventArgs args); + + public event NewEventHandler Pub; + + protected virtual void OnEvent(bool update = false) + { + if (Keep > 0) + { + TrimToSize(keep: Keep); + } + Pub?.Invoke(this, new TSeriesEventArgs { update = update }); + } + + /// common helpers + public static void BufferTrim(List buffer, double value, int period, bool update) + { + if (!update) + { + buffer.Add(value); + if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); } + return; + } + buffer[^1] = value; + } + public virtual void Reset() + { + } + + public void TrimToSize(int keep) + { + if (keep >= this.Count) + { + return; // No need to trim if the series is already smaller than or equal to n + } + + // Remove elements from the beginning of the list + int elementsToRemove = this.Count - keep; + RemoveRange(0, elementsToRemove); + } +} diff --git a/archive/Calculations/_Updated/VAR_Series.cs b/archive/Calculations/_Updated/VAR_Series.cs new file mode 100644 index 00000000..4f03e12d --- /dev/null +++ b/archive/Calculations/_Updated/VAR_Series.cs @@ -0,0 +1,90 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +VAR: Population Variance + Population variance without Bessel's correction + +Sources: + https://en.wikipedia.org/wiki/Variance + Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction + +Remark: + VAR (Population Variance) is also known as a biased Sample Variance. For unbiased + sample variance use SVAR instead. + + */ + +public class VAR_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public VAR_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"VAR({period})"; + } + public VAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public VAR_Series() : this(period: 0, useNaN: false) { } + public VAR_Series(int period) : this(period: period, useNaN: false) { } + public VAR_Series(TBars source) : this(source.Close, 0, false) { } + public VAR_Series(TBars source, int period) : this(source.Close, period, false) { } + public VAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public VAR_Series(TSeries source) : this(source, 0, false) { } + public VAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _pvar = 0; + for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } + _pvar /= this._buffer.Count; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pvar); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/WMAPE_Series.cs b/archive/Calculations/_Updated/WMAPE_Series.cs new file mode 100644 index 00000000..55935b85 --- /dev/null +++ b/archive/Calculations/_Updated/WMAPE_Series.cs @@ -0,0 +1,92 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +WMAPE: Weighted Mean Absolute Percentage Error + Measures the size of the error in percentage terms. Improves problems with MAPE + when there are zero or close-to-zero values because there would be a division by zero + or values of MAPE tending to infinity. + +Sources: + https://en.wikipedia.org/wiki/WMAPE + + */ + +public class WMAPE_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public WMAPE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"WMAPE({period})"; + } + public WMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public WMAPE_Series() : this(period: 0, useNaN: false) { } + public WMAPE_Series(int period) : this(period: period, useNaN: false) { } + public WMAPE_Series(TBars source) : this(source.Close, 0, false) { } + public WMAPE_Series(TBars source, int period) : this(source.Close, period, false) { } + public WMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public WMAPE_Series(TSeries source) : this(source, 0, false) { } + public WMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + + double _sma = _buffer.Average(); + + double _div = 0; + double _wmape = 0; + for (int i = 0; i < _buffer.Count; i++) + { + _wmape += Math.Abs(_buffer[i] - _sma); + _div += Math.Abs(_buffer[i]); + } + _wmape = (_div != 0) ? _wmape / _div : double.PositiveInfinity; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wmape); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/WMA_Series.cs b/archive/Calculations/_Updated/WMA_Series.cs new file mode 100644 index 00000000..7fbf16a1 --- /dev/null +++ b/archive/Calculations/_Updated/WMA_Series.cs @@ -0,0 +1,117 @@ +namespace QuanTAlib; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +/* +WMA: (linearly) Weighted Moving Average + The weights are linearly decreasing over the period and the most recent data has + the heaviest weight. + +Sources: + https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/weighted-moving-average-wma/ + https://www.technicalindicators.net/indicators-technical-analysis/83-moving-averages-simple-exponential-weighted + + */ + +public class WMA_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + private System.Collections.Generic.List _weights; + protected int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + protected int _len; + public int Len + { + get { return _len; } + set { _len = value; } + } + + //core constructors + public WMA_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"WMA({period})"; + _len = 1; + _weights = CalculateWeights(_period); + } + public WMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public WMA_Series() : this(period: 0, useNaN: false) { } + public WMA_Series(int period) : this(period: period, useNaN: false) { } + public WMA_Series(TBars source) : this(source.Close, 0, false) { } + public WMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public WMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public WMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + if (_period == 0) + { + _weights = CalculateWeights(_len); + _len++; + } + double _wma = 0; + double totalWeights = (_buffer.Count * (_buffer.Count + 1)) * 0.5; + object lockObj = new object(); + Parallel.For(0, _buffer.Count, i => + { + double temp = _buffer[i] * this._weights[i]; + lock (lockObj) { _wma += temp; } + }); + _wma /= totalWeights; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wma); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //calculating weights + private static List CalculateWeights(int period) + { + List weights = new List(period); + for (int i = 0; i < period; i++) + { + weights.Add(i + 1); + } + return weights; + } + + //reset calculation + public override void Reset() + { + _len = 0; + _weights = CalculateWeights(_period); + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ZLEMA_Series.cs b/archive/Calculations/_Updated/ZLEMA_Series.cs new file mode 100644 index 00000000..676e7fe5 --- /dev/null +++ b/archive/Calculations/_Updated/ZLEMA_Series.cs @@ -0,0 +1,105 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +ZLEMA: Zero Lag Exponential Moving Average + The Zero lag exponential moving average (ZLEMA) indicator was created by John + Ehlers and Ric Way. + +The formula for a given N-Day period and for a given Data series is: + Lag = (Period-1)/2 + Ema Data = {Data+(Data-Data(Lag days ago)) + ZLEMA = EMA (EmaData,Period) + +Remark: + The idea is do a regular exponential moving average (EMA) calculation but on a + de-lagged data instead of doing it on the regular data. Data is de-lagged by + removing the data from "lag" days ago thus removing (or attempting to remove) + the cumulative lag effect of the moving average. + + */ + +public class ZLEMA_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + private int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly EMA_Series _ema; + + //core constructor + public ZLEMA_Series(int period, bool useNaN, bool useSMA) + { + _period = period; + _NaN = useNaN; + Name = $"ZLEMA({period})"; + _len = 1; + _ema = new(period); + } + //generic constructors (source) + + public ZLEMA_Series() : this(0, false, true) { } + public ZLEMA_Series(int period) : this(period, false, true) { } + public ZLEMA_Series(TBars source) : this(source.Close, 0, false) { } + public ZLEMA_Series(TBars source, int period) : this(source.Close, period, false) { } + public ZLEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public ZLEMA_Series(TSeries source, int period) : this(source, period, false, true) { } + public ZLEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } + public ZLEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + int _lag; + if (_period == 0) + { + _lag = (int)((_len - 1) * 0.5); + _len++; + } + else { _lag = (int)((_period - 1) * 0.5); } + _lag = Math.Min(_lag, _buffer.Count - 1); + _lag = Math.Max(_lag, 0) + 1; + double _zlValue = 2 * TValue.v - _buffer[^_lag]; + double _zlema = _ema.Add((TValue.t, _zlValue), update).v; + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlema); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + _ema.Reset(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ZL_Series.cs b/archive/Calculations/_Updated/ZL_Series.cs new file mode 100644 index 00000000..ee7aa4ce --- /dev/null +++ b/archive/Calculations/_Updated/ZL_Series.cs @@ -0,0 +1,100 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +/* +ZL: Zero Lag + Data is de-lagged by removing the data from “lag” days ago, thus removing + (or attempting to) the cumulative effect of the moving average. + +Calculation: + Lag = (Period-1)/2 + ZL = Data + (Data - Data(Lag days ago) ) + +Sources: + https://mudrex.com/blog/zero-lag-ema-trading-strategy/ + + */ + +public class ZL_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + private int _len; + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + private readonly EMA_Series _ema; + + //core constructor + public ZL_Series(int period, bool useNaN, bool useSMA) + { + _period = period; + _NaN = useNaN; + Name = $"ZL({period})"; + _len = 1; + _ema = new(period); + } + //generic constructors (source) + + public ZL_Series() : this(0, false, true) { } + public ZL_Series(int period) : this(period, false, true) { } + public ZL_Series(TBars source) : this(source.Close, 0, false) { } + public ZL_Series(TBars source, int period) : this(source.Close, period, false) { } + public ZL_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public ZL_Series(TSeries source, int period) : this(source, period, false, true) { } + public ZL_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { } + public ZL_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + int _lag; + if (_period == 0) + { + _lag = (int)((_len - 1) * 0.5); + _len++; + } + else { _lag = (int)((_period - 1) * 0.5); } + _lag = Math.Min(_lag, _buffer.Count - 1); + _lag = Math.Max(_lag, 0) + 1; + double _zlValue = 2 * TValue.v - _buffer[^_lag]; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlValue); + return base.Add(res, update); + } + + //variation of Add() + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + _ema.Reset(); + } +} \ No newline at end of file diff --git a/archive/Calculations/_Updated/ZSCORE_Series.cs b/archive/Calculations/_Updated/ZSCORE_Series.cs new file mode 100644 index 00000000..52c45d70 --- /dev/null +++ b/archive/Calculations/_Updated/ZSCORE_Series.cs @@ -0,0 +1,97 @@ +using System.Linq; + +namespace QuanTAlib; +using System; +using System.Collections.Generic; + +/* +ZSCORE: number of standard deviations from SMA + Z-score describes a value's relationship to the mean of a series, as measured in + terms of standard deviations from the mean. If a Z-score is 0, it indicates that + the data point's score is identical to the mean score. A Z-score of 1.0 would + indicate a value that is one standard deviation from the mean. Z-scores may be + positive or negative, with a positive value indicating the score is above the + mean and a negative score indicating it is below the mean. + +Sources: + https://en.wikipedia.org/wiki/Z-score + https://www.investopedia.com/terms/z/zscore.asp + +Calculation: + std = std * STDEV(close, length) + mean = SMA(close, length) + ZSCORE = (close - mean) / std + + */ + +public class ZSCORE_Series : TSeries +{ + private readonly System.Collections.Generic.List _buffer = new(); + protected readonly int _period; + protected readonly bool _NaN; + protected readonly TSeries _data; + + //core constructors + public ZSCORE_Series(int period, bool useNaN) + { + _period = period; + _NaN = useNaN; + Name = $"ZSCORE({period})"; + } + public ZSCORE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) + { + _data = source; + Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})"; + _data.Pub += Sub; + Add(_data); + } + public ZSCORE_Series() : this(period: 0, useNaN: false) { } + public ZSCORE_Series(int period) : this(period: period, useNaN: false) { } + public ZSCORE_Series(TBars source) : this(source.Close, 0, false) { } + public ZSCORE_Series(TBars source, int period) : this(source.Close, period, false) { } + public ZSCORE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { } + public ZSCORE_Series(TSeries source) : this(source, 0, false) { } + public ZSCORE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { } + + ////////////////// + // core Add() algo + public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) + { + BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update); + double _sma = _buffer.Average(); + + double _pvar = 0; + for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); } + _pvar /= this._buffer.Count; + double _psdev = Math.Sqrt(_pvar); + double _zscore = (_psdev == 0) ? 1 : (TValue.v - _sma) / _psdev; + + var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zscore); + return base.Add(res, update); + } + + public override (DateTime t, double v) Add(TSeries data) + { + if (data == null) { return (DateTime.Today, Double.NaN); } + foreach (var item in data) { Add(item, false); } + return _data.Last; + } + public (DateTime t, double v) Add(bool update) + { + return this.Add(TValue: _data.Last, update: update); + } + public (DateTime t, double v) Add() + { + return Add(TValue: _data.Last, update: false); + } + private new void Sub(object source, TSeriesEventArgs e) + { + Add(TValue: _data.Last, update: e.update); + } + + //reset calculation + public override void Reset() + { + _buffer.Clear(); + } +} \ No newline at end of file diff --git a/archive/Indicators/Charts/2MACross_chart.cs b/archive/Indicators/Charts/2MACross_chart.cs new file mode 100644 index 00000000..d655896b --- /dev/null +++ b/archive/Indicators/Charts/2MACross_chart.cs @@ -0,0 +1,298 @@ +using System; +using System.Drawing; +using System.Linq; +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MovingAverage_chart : Indicator +{ + #region Parameters + [InputParameter("MA1: Type:", 0, variants: new object[] + { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, + "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] + private int MA1type = 15; + + [InputParameter("MA1: Smoothing period:", 1, 1, 999, 1, 1)] + private int MA1Period = 10; + + [InputParameter("MA1: Data source:", 2, variants: new object[] + { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, + "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] + private int MA1DataSource = 3; + + [InputParameter("MA2: Type:", 3, variants: new object[] + { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, + "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] + private int MA2type = 16; + + [InputParameter("MA2: Smoothing period:", 4, 1, 999, 1, 1)] + private int MA2Period = 50; + + [InputParameter("MA2: Data source:", 5, variants: new object[] + { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, + "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] + private int MA2DataSource = 8; + + [InputParameter("Long trades", 6)] + private bool LongTrades = true; + + [InputParameter("Short trades", 6)] + private bool ShortTrades = true; + + #endregion Parameters + + protected HistoricalData History; + private TBars bars; + + /////// + private TSeries MA1, MA2; + private CROSS_Series trades; + private COMPARE_Series overunder; + + /////// + + public MovingAverage_chart() + { + this.SeparateWindow = false; + this.Name = "MAs Crossover"; + this.AddLineSeries("MA1", Color.LimeGreen, 2, LineStyle.Solid); + this.AddLineSeries("MA2", Color.OrangeRed, 2, LineStyle.Solid); + } + + protected override void OnInit() + { + this.bars = new(); + this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); + for (int i = this.History.Count - 1; i >= 0; i--) + { + var rec = this.History[i, SeekOriginHistory.Begin]; + bars.Add(rec.TimeLeft, rec[PriceType.Open], + rec[PriceType.High], rec[PriceType.Low], + rec[PriceType.Close], rec[PriceType.Volume]); + } + this.Name = "MAs Cross: [ "; + switch (MA1type) + { + case 0: + MA1 = new SMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"SMA"; + break; + case 1: + MA1 = new EMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"EMA"; + break; + case 2: + MA1 = new WMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"WMA"; + break; + case 3: + MA1 = new T3_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"T3"; + break; + case 4: + MA1 = new SMMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"SMMA"; + break; + case 5: + MA1 = new TRIMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"TRIMA"; + break; + case 6: + MA1 = new DWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"DWMA"; + break; + case 7: + MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period); + this.Name += $"FWMA"; + break; + case 8: + MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"DEMA"; + break; + case 9: + MA1 = new TEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"TEMA"; + break; + case 10: + MA1 = new ALMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"ALMA"; + break; + case 11: + MA1 = new HMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"HMA"; + break; + case 12: + MA1 = new HEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"HEMA"; + break; + case 13: + double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA1Period); + MA1 = new MAMA_Series(source: bars.Select(this.MA1DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); + this.Name += $"MAMA"; + break; + case 14: + MA1 = new KAMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"KAMA"; + break; + case 15: + MA1 = new ZLEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"ZLEMA"; + break; + default: + MA1 = new JMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"JMA"; + break; + } + + this.Name = this.Name + $" ({MA1Period}:{TBars.SelectStr(this.MA1DataSource)}) : "; + + switch (MA2type) + { + case 0: + MA2 = new SMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"SMA"; + break; + case 1: + MA2 = new EMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"EMA"; + break; + case 2: + MA2 = new WMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"WMA"; + break; + case 3: + MA2 = new T3_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"T3"; + break; + case 4: + MA2 = new SMMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"SMMA"; + break; + case 5: + MA2 = new TRIMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"TRIMA"; + break; + case 6: + MA2 = new DWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"DWMA"; + break; + case 7: + MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period); + this.Name += $"FWMA"; + break; + case 8: + MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"DEMA"; + break; + case 9: + MA2 = new TEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"TEMA"; + break; + case 10: + MA2 = new ALMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"ALMA"; + break; + case 11: + MA2 = new HMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"HMA"; + break; + case 12: + MA2 = new HEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"HEMA"; + break; + case 13: + double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA2Period); + MA2 = new MAMA_Series(source: bars.Select(this.MA2DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); + this.Name += $"MAMA"; + break; + case 14: + MA2 = new KAMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"KAMA"; + break; + case 15: + MA2 = new ZLEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"ZLEMA"; + break; + default: + MA2 = new JMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"JMA"; + break; + } + this.Name += $"({MA2Period}:{TBars.SelectStr(this.MA2DataSource)}) ]"; + + int maxKeep = Math.Max(Math.Max(this.MA1Period, this.MA2Period), 100); + MA1.Keep = maxKeep; + MA2.Keep = maxKeep; + trades.Keep = maxKeep; + overunder.Keep = maxKeep; + + overunder = new(MA1, MA2); + trades = new(MA1, MA2); + } + + protected override void OnUpdate(UpdateArgs args) + { + bool update = !(args.Reason == UpdateReason.NewBar || + args.Reason == UpdateReason.HistoricalBar); + this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), + this.GetPrice(PriceType.High), + this.GetPrice(PriceType.Low), + this.GetPrice(PriceType.Close), + this.GetPrice(PriceType.Volume), update); + this.SetValue(this.MA1[^1].v, lineIndex: 0); + this.SetValue(this.MA2[^1].v, lineIndex: 1); + + if (trades[^1].v == 1) + { + this.EndCloud(0, 1, Color.Empty); + if (LongTrades) + { + this.LinesSeries[0].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow)); + this.BeginCloud(0, 1, Color.FromArgb(127, Color.Green)); + } + if (ShortTrades) + { + this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow)); + } + } + if (trades[^1].v == -1) + { + this.EndCloud(0, 1, Color.Empty); + if (ShortTrades) + { + this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow)); + this.BeginCloud(0, 1, Color.FromArgb(127, Color.Red)); + } + if (LongTrades) + { + this.LinesSeries[0].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); + } + } + } + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + if (this.CurrentChart == null) { return; } + Graphics graphics = args.Graphics; + var mainWindow = this.CurrentChart.MainWindow; + int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); + int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); + int historycount = HistoricalData.Count; + int ymax = mainWindow.ClientRectangle.Height; + int xmax = mainWindow.ClientRectangle.Width; + + /* + for (int i = leftIndex; i <= rightIndex; i++) { + int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); + int width = this.CurrentChart.BarsWidth; + int height = (int)((equity[i+historycount].v) *proportion); + + Brush bb = Brushes.DarkSlateGray; + bb = (overunder[i+historycount].v>0 && LongTrades)? Brushes.Green : bb; + bb = (overunder[i + historycount].v < 0 && ShortTrades) ? Brushes.Red : bb; + + graphics.FillRectangle(bb, xi, ymax - height, width, height); + } + */ + } +} diff --git a/archive/Indicators/Charts/2MASlope_chart.cs b/archive/Indicators/Charts/2MASlope_chart.cs new file mode 100644 index 00000000..db75d157 --- /dev/null +++ b/archive/Indicators/Charts/2MASlope_chart.cs @@ -0,0 +1,320 @@ +using System; +using System.Drawing; +using System.Linq; +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MovingAverageSlope_chart : Indicator +{ + #region Parameters + [InputParameter("MA1: Type:", 0, variants: new object[] + { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, + "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] + private int MA1type = 16; + + [InputParameter("MA1: Smoothing period:", 1, 1, 999, 1, 1)] + private int MA1Period = 10; + + [InputParameter("MA1: Data source:", 2, variants: new object[] + { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, + "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] + private int MA1DataSource = 3; + + [InputParameter("MA2: Type:", 3, variants: new object[] + { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9, + "ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})] + private int MA2type = 6; + + [InputParameter("MA2: Smoothing period:", 4, 1, 999, 1, 1)] + private int MA2Period = 50; + + [InputParameter("MA2: Data source:", 5, variants: new object[] + { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, + "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] + private int MA2DataSource = 8; + + [InputParameter("Data required for slope calc:", 6, 2, 10, 1, 1)] + private int SlopePeriod = 3; + + [InputParameter("Long trades", 7)] + private bool LongTrades = true; + + [InputParameter("Short trades", 8)] + private bool ShortTrades; + + #endregion Parameters + + protected HistoricalData History; + private TBars bars; + + /////// + private TSeries MA1, MA2; + private SLOPE_Series sMA1, sMA2; + private CROSS_Series sig1, sig2; + + private bool inLong, inShort; + /////// + + public MovingAverageSlope_chart() + { + this.SeparateWindow = false; + this.Name = "Slopes convergence"; + this.AddLineSeries("MA1", Color.DarkSlateGray, 2, LineStyle.Solid); + this.AddLineSeries("MA2", Color.DarkSlateGray, 2, LineStyle.Solid); + } + + protected override void OnInit() + { + this.bars = new(); + this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); + for (int i = this.History.Count - 1; i >= 0; i--) + { + var rec = this.History[i, SeekOriginHistory.Begin]; + bars.Add(rec.TimeLeft, rec[PriceType.Open], + rec[PriceType.High], rec[PriceType.Low], + rec[PriceType.Close], rec[PriceType.Volume]); + } + this.Name = "Slopes convergence: [ "; + switch (MA1type) + { + case 0: + MA1 = new SMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"SMA"; + break; + case 1: + MA1 = new EMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"EMA"; + break; + case 2: + MA1 = new WMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"WMA"; + break; + case 3: + MA1 = new T3_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"T3"; + break; + case 4: + MA1 = new SMMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"SMMA"; + break; + case 5: + MA1 = new TRIMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"TRIMA"; + break; + case 6: + MA1 = new DWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"DWMA"; + break; + case 7: + MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period); + this.Name += $"FWMA"; + break; + case 8: + MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"DEMA"; + break; + case 9: + MA1 = new TEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"TEMA"; + break; + case 10: + MA1 = new ALMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"ALMA"; + break; + case 11: + MA1 = new HMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"HMA"; + break; + case 12: + MA1 = new HEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"HEMA"; + break; + case 13: + double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA1Period); + MA1 = new MAMA_Series(source: bars.Select(this.MA1DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); + this.Name += $"MAMA"; + break; + case 14: + MA1 = new KAMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"KAMA"; + break; + case 15: + MA1 = new ZLEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"ZLEMA"; + break; + default: + MA1 = new JMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false); + this.Name += $"JMA"; + break; + } + + this.Name = this.Name + $" ({MA1Period}:{TBars.SelectStr(this.MA1DataSource)}) : "; + + switch (MA2type) + { + case 0: + MA2 = new SMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"SMA"; + break; + case 1: + MA2 = new EMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"EMA"; + break; + case 2: + MA2 = new WMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"WMA"; + break; + case 3: + MA2 = new T3_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"T3"; + break; + case 4: + MA2 = new SMMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"SMMA"; + break; + case 5: + MA2 = new TRIMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"TRIMA"; + break; + case 6: + MA2 = new DWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"DWMA"; + break; + case 7: + MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period); + this.Name += $"FWMA"; + break; + case 8: + MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"DEMA"; + break; + case 9: + MA2 = new TEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"TEMA"; + break; + case 10: + MA2 = new ALMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"ALMA"; + break; + case 11: + MA2 = new HMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"HMA"; + break; + case 12: + MA2 = new HEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"HEMA"; + break; + case 13: + double factor = 1.015 * Math.Exp(-0.043 * (double)this.MA2Period); + MA2 = new MAMA_Series(source: bars.Select(this.MA2DataSource), fastlimit: factor, slowlimit: factor * 0.1, useNaN: false); + this.Name += $"MAMA"; + break; + case 14: + MA2 = new KAMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"KAMA"; + break; + case 15: + MA2 = new ZLEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"ZLEMA"; + break; + default: + MA2 = new JMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false); + this.Name += $"JMA"; + break; + } + this.Name += $"({MA2Period}:{TBars.SelectStr(this.MA2DataSource)}) ]"; + + sMA1 = new(MA1, SlopePeriod); + sMA2 = new(MA2, SlopePeriod); + sig1 = new(sMA1, 0); + sig2 = new(sMA2, 0); + + int maxKeep = Math.Max(Math.Max(this.MA1Period, this.MA2Period), 100); + + MA1.Keep = maxKeep; + MA2.Keep = maxKeep; + sMA1.Keep = maxKeep; + sMA2.Keep = maxKeep; + sig1.Keep = maxKeep; + sig2.Keep = maxKeep; + } + + protected override void OnUpdate(UpdateArgs args) + { + bool update = !(args.Reason == UpdateReason.NewBar || + args.Reason == UpdateReason.HistoricalBar); + this.bars.Add(this.Time(), this.Open(), this.High(), this.Low(), this.Close(), this.Volume(), update); + this.SetValue(this.MA1[^1].v, lineIndex: 0); + this.SetValue(this.MA2[^1].v, lineIndex: 1); + + Color s1Color = (this.sMA1[^1].v > 0) ? Color.LimeGreen : Color.OrangeRed; + Color s2Color = (this.sMA2[^1].v > 0) ? Color.LimeGreen : Color.OrangeRed; + + this.LinesSeries[0].SetMarker(0, s1Color); + this.LinesSeries[1].SetMarker(0, s2Color); + + if (sig1[^1].v > 0 || sig2[^1].v > 0) + { + if (sMA1[^1].v >= 0 && sMA2[^1].v >= 0 && LongTrades) + { + inLong = true; + this.BeginCloud(0, 1, Color.FromArgb(127, Color.DarkGreen)); + this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 0 : 1].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow)); + } + else + { + this.EndCloud(0, 1, Color.Empty); + if (inShort && this.Count > 1) + { + this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow)); + inShort = false; + } + } + } + + if (sig1[^1].v < 0 || sig2[^1].v < 0) + { + if (sMA1[^1].v <= 0 && sMA2[^1].v <= 0 && ShortTrades) + { + inShort = true; + this.BeginCloud(0, 1, Color.FromArgb(100, Color.Red)); + this.LinesSeries[(this.MA1[^1].v > this.MA2[^1].v) ? 0 : 1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow)); + } + else + { + this.EndCloud(0, 1, Color.Empty); + if (inLong && this.Count > 1) + { + LinesSeries[(this.MA1[^1].v > this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); + inLong = false; + } + } + } + } + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + if (this.CurrentChart == null) { return; } + Graphics graphics = args.Graphics; + var mainWindow = this.CurrentChart.MainWindow; + int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); + int rightIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right))); + /* + int historycount = HistoricalData.Count; + int ymax = mainWindow.ClientRectangle.Height; + + + for (int i = leftIndex; i <= rightIndex; i++) { + int xi = (int)Math.Round(mainWindow.CoordinatesConverter.GetChartX(Time(Count - 1 - i))); + int width = this.CurrentChart.BarsWidth; + int height = (int)((equity[i+historycount].v) *proportion); + + Brush bb = Brushes.DarkSlateGray; + bb = (overunder[i+historycount].v>0 && LongTrades)? Brushes.Green : bb; + bb = (overunder[i + historycount].v < 0 && ShortTrades) ? Brushes.Red : bb; + + graphics.FillRectangle(bb, xi, ymax - height, width, height); + } + */ + } +} diff --git a/archive/Indicators/Charts/JMA_chart.cs b/archive/Indicators/Charts/JMA_chart.cs new file mode 100644 index 00000000..1f7ec9aa --- /dev/null +++ b/archive/Indicators/Charts/JMA_chart.cs @@ -0,0 +1,104 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using TradingPlatform.BusinessLayer; +using TradingPlatform.BusinessLayer.Chart; +namespace QuanTAlib; + +public class JMA_chart : Indicator +{ + #region Parameters + + [InputParameter("Data source", 0, variants: new object[] + { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, + "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] + private int DataSource = 3; + + [InputParameter("Smoothing period", 1, 1, 999, 1, 1)] + private int Period = 9; + + [InputParameter("Volatility short", 2, 3, 50, 1, 1)] + private int Vshort = 10; + + [InputParameter("Volatility long", 3, 20, 500, 1, 1)] + private int Vlong = 65; + + [InputParameter("Phase", 4, -100, 100, 1, 2)] + private double Jphase; + + #endregion Parameters + + /////// + private JMA_Series indicator; + /////// + + protected TBars bars; + protected IChartWindow mainWindow; + protected Graphics graphics; + protected int firstOnScreenBarIndex, lastOnScreenBarIndex; + protected HistoricalData History; + protected int HistPeriod; + public JMA_chart() + { + Name = "JMA - Jurik Moving Avg"; + Description = "Jurik Moving Average description"; + AddLineSeries(lineName: "JMA", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); + SeparateWindow = false; + HistPeriod = Period; + } + + + protected override void OnInit() + { + base.OnInit(); + bars = new(); + var dur1 = this.HistoricalData.FromTime; + var dur = this.HistoricalData.Period.Duration.TotalSeconds * (HistPeriod * 4); //seconds of two periods + + this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); + + for (int i = this.History.Count - 1; i >= 0; i--) + { + + var rec = this.History[i, SeekOriginHistory.Begin]; + + bars.Add(rec.TimeLeft, rec[PriceType.Open], + rec[PriceType.High], rec[PriceType.Low], + rec[PriceType.Close], rec[PriceType.Volume]); + } + + indicator = new(source: bars.Select(DataSource), period: Period, phase: Jphase, vshort: Vshort, vlong: Vlong, useNaN: true); + indicator.Keep = Math.Max(Period, 100); + } + + protected override void OnUpdate(UpdateArgs args) + { + base.OnUpdate(args); + bars.Add(Time(), GetPrice(PriceType.Open), + GetPrice(PriceType.High), + GetPrice(PriceType.Low), + GetPrice(PriceType.Close), + GetPrice(PriceType.Volume), + update: !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar)); + + this.SetValue(indicator[^1].v, lineIndex: 0); + } + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + if (this.CurrentChart == null) + { + return; + } + + graphics = args.Graphics; + mainWindow = this.CurrentChart.MainWindow; + + DateTime leftTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left); + DateTime rightTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right); + firstOnScreenBarIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(leftTime); + lastOnScreenBarIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(rightTime)); + } + +} diff --git a/archive/Indicators/Charts/TrailingStop.cs b/archive/Indicators/Charts/TrailingStop.cs new file mode 100644 index 00000000..ad480826 --- /dev/null +++ b/archive/Indicators/Charts/TrailingStop.cs @@ -0,0 +1,102 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class TrailingStop_chart : Indicator +{ + #region Parameters + + [InputParameter("Period", 0, 1, 100, 1, 1)] + protected int _period = 30; + + [InputParameter("Factor", 1, 1, 100, 0.1, 1)] + protected double _factor = 10; + + [InputParameter("Long TS", 2)] + private bool _LongTS = true; + + [InputParameter("Short TS", 3)] + private bool _ShortTS = true; + + #endregion Parameters + + /////// + private HistoricalData History; + private TBars bars; + private ATR_Series _atr; + private double _tslineL, _ratchetL, _tslineS, _ratchetS; + + /////// + + public TrailingStop_chart() + { + Name = $"ATR Trailing Stop"; + AddLineSeries(lineName: "TrailingATR Long", lineColor: Color.Yellow, lineWidth: 1, lineStyle: LineStyle.Dot); + AddLineSeries(lineName: "Ratchet Long", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); + + AddLineSeries(lineName: "TrailingATR Short", lineColor: Color.Yellow, lineWidth: 1, lineStyle: LineStyle.Dot); + AddLineSeries(lineName: "Ratchet Short", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); + + SeparateWindow = false; + } + + + protected override void OnInit() + { + this.Name = $"Trailing Stop (ATR:{_period}, Mult:{_factor:f2})"; + this.bars = new(); + + this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); + for (int i = this.History.Count - 1; i >= 0; i--) + { + var rec = this.History[i, SeekOriginHistory.Begin]; + bars.Add(rec.TimeLeft, rec[PriceType.Open], + rec[PriceType.High], rec[PriceType.Low], + rec[PriceType.Close], rec[PriceType.Volume]); + } + _atr = new(source: bars, _period, useNaN: true); + _ratchetL = Double.NegativeInfinity; + _ratchetS = Double.PositiveInfinity; + + this.LinesSeries[0].Visible = _LongTS; + this.LinesSeries[1].Visible = _LongTS; + this.LinesSeries[2].Visible = _ShortTS; + this.LinesSeries[3].Visible = _ShortTS; + } + + protected override void OnUpdate(UpdateArgs args) + { + bool update = !(args.Reason == UpdateReason.NewBar || + args.Reason == UpdateReason.HistoricalBar); + this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), + this.GetPrice(PriceType.High), + this.GetPrice(PriceType.Low), + this.GetPrice(PriceType.Close), + this.GetPrice(PriceType.Volume), update); + + _tslineL = bars.High[^1].v - (_factor * _atr[^1].v); + _ratchetL = Math.Max(_tslineL, _ratchetL); + if (_ratchetL > bars.Low[^1].v) + { + this.LinesSeries[1].SetMarker(0, new IndicatorLineMarker(Color.Yellow, bottomIcon: IndicatorLineMarkerIconType.DownArrow)); + _ratchetL = _tslineL; + } + + _tslineS = bars.High[^1].v + (_factor * _atr[^1].v); + _ratchetS = Math.Min(_tslineS, _ratchetS); + if (_ratchetS < bars.High[^1].v) + { + this.LinesSeries[3].SetMarker(0, new IndicatorLineMarker(Color.Yellow, upperIcon: IndicatorLineMarkerIconType.UpArrow)); + _ratchetS = _tslineS; + } + + this.SetValue(_tslineL, lineIndex: 0); + this.SetValue(_ratchetL, lineIndex: 1); + this.SetValue(_tslineS, lineIndex: 2); + this.SetValue(_ratchetS, lineIndex: 3); + } +} + diff --git a/Indicators/Indicators.csproj b/archive/Indicators/Indicators.csproj similarity index 97% rename from Indicators/Indicators.csproj rename to archive/Indicators/Indicators.csproj index eaa8bbb0..5b9da593 100644 --- a/Indicators/Indicators.csproj +++ b/archive/Indicators/Indicators.csproj @@ -1,56 +1,56 @@ - - - net7.0 - preview - false - AnyCPU - Indicator - QuanTAlib_Indicators - QuanTAlib - embedded - AnyCPU - disable - False - ..\.sonarlint\mihakralj_quantalibcsharp.ruleset - 0.2.1.0 - 0.2.1.0 - 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d - 0.2.1-dev.2 - NETSDK1057 - true - NETSDK1057 - - - True - 3 - True - anycpu - full - - - embedded - True - 3 - True - anycpu - - - - - - - - - - - - - QuanTAlib\%(RecursiveDir)%(Filename)%(Extension) - - - - - ..\.github\TradingPlatform.BusinessLayer.dll - - + + + net7.0 + preview + false + AnyCPU + Indicator + QuanTAlib_Indicators + QuanTAlib + embedded + AnyCPU + disable + False + ..\.sonarlint\mihakralj_quantalibcsharp.ruleset + 0.2.1.0 + 0.2.1.0 + 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d + 0.2.1-dev.2 + NETSDK1057 + true + NETSDK1057 + + + True + 3 + True + anycpu + full + + + embedded + True + 3 + True + anycpu + + + + + + + + + + + + + QuanTAlib\%(RecursiveDir)%(Filename)%(Extension) + + + + + ..\.github\TradingPlatform.BusinessLayer.dll + + \ No newline at end of file diff --git a/archive/QuanTAlib.sln_old b/archive/QuanTAlib.sln_old new file mode 100644 index 00000000..a2d92cf3 --- /dev/null +++ b/archive/QuanTAlib.sln_old @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32210.308 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calculations", "v2\calculations.csproj", "{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E5592DC2-0542-45B2-A0CF-C6B1EDC72B87} + EndGlobalSection +EndGlobal \ No newline at end of file diff --git a/Strategies/Strategies.csproj b/archive/Strategies/Strategies.csproj similarity index 97% rename from Strategies/Strategies.csproj rename to archive/Strategies/Strategies.csproj index 5cf21f57..e665dc50 100644 --- a/Strategies/Strategies.csproj +++ b/archive/Strategies/Strategies.csproj @@ -1,53 +1,53 @@ - - - net7.0 - preview - false - AnyCPU - Strategy - QuanTAlib_Strategies - QuanTAlib - embedded - AnyCPU - disable - False - ..\.sonarlint\mihakralj_quantalibcsharp.ruleset - 0.2.1.0 - 0.2.1.0 - 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d - 0.2.1-dev.2 - NETSDK1057 - true - NETSDK1057 - - - True - 3 - True - anycpu - full - - - embedded - True - 3 - True - anycpu - - - - - - - QuanTAlib\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - - ..\.github\TradingPlatform.BusinessLayer.dll - - + + + net7.0 + preview + false + AnyCPU + Strategy + QuanTAlib_Strategies + QuanTAlib + embedded + AnyCPU + disable + False + ..\.sonarlint\mihakralj_quantalibcsharp.ruleset + 0.2.1.0 + 0.2.1.0 + 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d + 0.2.1-dev.2 + NETSDK1057 + true + NETSDK1057 + + + True + 3 + True + anycpu + full + + + embedded + True + 3 + True + anycpu + + + + + + + QuanTAlib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + ..\.github\TradingPlatform.BusinessLayer.dll + + \ No newline at end of file diff --git a/archive/Tests/Basic tests/Indicators.cs b/archive/Tests/Basic tests/Indicators.cs new file mode 100644 index 00000000..431aa94f --- /dev/null +++ b/archive/Tests/Basic tests/Indicators.cs @@ -0,0 +1,156 @@ +using Xunit; +using System; +using QuanTAlib; + +namespace Basics; +#nullable disable +public class Indicators +{ + private static Type[] maSeriesTypes = new Type[] + { + typeof(SMA_Series), + typeof(EMA_Series), + typeof(DEMA_Series), + typeof(TEMA_Series), + typeof(WMA_Series), + typeof(ALMA_Series), + typeof(DWMA_Series), + typeof(FWMA_Series), + typeof(HMA_Series), + typeof(ZLEMA_Series), + typeof(RMA_Series), + typeof(HEMA_Series), + typeof(JMA_Series), + typeof(CUSUM_Series), + typeof(SMMA_Series), + typeof(T3_Series), + typeof(KAMA_Series), + typeof(TRIMA_Series), + typeof(MAMA_Series), + typeof(HWMA_Series), + }; + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Name_exists(Type classType) + { + TSeries data = new("Data") { 1, 2, 3 }; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + Assert.NotEmpty(MA_Series.Name); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Series_Length(Type classType) + { + GBM_Feed feed = new(1000); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + Assert.Equal(1000, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Return_data(Type classType) + { + TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + var result = MA_Series.Add(20); + Assert.Equal(result.v, MA_Series.Last.v); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Update(Type classType) + { + TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + var pre_update = MA_Series.Last.v; + + double pre_data = data.Last.v; + data.Add(20, true); + data.Add(pre_data, true); + + Assert.Equal(pre_update, MA_Series.Last.v); + Assert.Equal(data.Count, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Period_zero(Type classType) + { + GBM_Feed feed = new(100); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries; + Assert.Equal(data.Count, MA_Series.Count); + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Reset(Type classType) + { + GBM_Feed feed = new(10); + TSeries data = feed.OHLC4; + var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries; + MA_Series.Reset(); + data.Add(0); + Assert.Equal(data.Last.v, MA_Series.Last.v); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Period_one(Type classType) + { + GBM_Feed feed = new(100); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries; + Assert.InRange(MA_Series.Last.v - data.Last.v, -10e-6, 10e-6); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void NaN_test(Type classType) + { + GBM_Feed feed = new(100); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; + Assert.True(double.IsNaN(MA_Series[0].v)); + Assert.True(double.IsNaN(MA_Series[8].v)); + Assert.False(double.IsNaN(MA_Series[9].v)); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Edge_numbers(Type classType) + { + TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity }; + var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; + Assert.Equal(4, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void handling_NaN(Type classType) + { + TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + public static IEnumerable MASeriesData() + { + foreach (var type in maSeriesTypes) + { + yield return new object[] { type }; + } + } +} +#nullable restore \ No newline at end of file diff --git a/archive/Tests/Basic tests/Oscillators.cs b/archive/Tests/Basic tests/Oscillators.cs new file mode 100644 index 00000000..fcf86f06 --- /dev/null +++ b/archive/Tests/Basic tests/Oscillators.cs @@ -0,0 +1,161 @@ +using Xunit; +using System; +using System.Runtime.InteropServices; +using QuanTAlib; + +namespace Basics; +#nullable disable +public class Oscillators +{ + private static Type[] maSeriesTypes = new[] + { + typeof(BIAS_Series), + typeof(MAX_Series), + typeof(MIN_Series), + typeof(MIDPOINT_Series), + typeof(ZL_Series), + typeof(DECAY_Series), + typeof(ENTROPY_Series), + typeof(KURTOSIS_Series), + typeof(MAD_Series), + typeof(MAPE_Series), + typeof(MAE_Series), + typeof(MSE_Series), + typeof(SDEV_Series), + typeof(SMAPE_Series), + typeof(WMAPE_Series), + typeof(SSDEV_Series), + typeof(VAR_Series), + typeof(SVAR_Series), + typeof(MEDIAN_Series), + typeof(ZSCORE_Series), + typeof(CMO_Series), + typeof(RSI_Series), + typeof(TRIX_Series), + typeof(BBANDS_Series), +}; + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Name_exists(Type classType) + { + TSeries data = new("Data") { 1, 2, 3 }; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + Assert.NotEmpty(MA_Series.Name); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Series_Length(Type classType) + { + GBM_Feed feed = new(1000); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + Assert.Equal(1000, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Return_data(Type classType) + { + TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + var result = MA_Series.Add(20); + Assert.Equal(result.v, MA_Series.Last.v); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Update(Type classType) + { + TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries; + var pre_update = MA_Series.Last.v; + + double pre_data = data.Last.v; + data.Add(20, true); + data.Add(pre_data, true); + + Assert.Equal(pre_update, MA_Series.Last.v); + Assert.Equal(data.Count, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Period_zero(Type classType) + { + GBM_Feed feed = new(100); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries; + Assert.Equal(data.Count, MA_Series.Count); + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Reset(Type classType) + { + GBM_Feed feed = new(10); + TSeries data = feed.OHLC4; + var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries; + MA_Series.Reset(); + data.Add(1); + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Period_one(Type classType) + { + GBM_Feed feed = new(100); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries; + Assert.False(double.IsNaN(MA_Series[^1].v)); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void NaN_test(Type classType) + { + GBM_Feed feed = new(100); + TSeries data = feed.OHLC4; + + var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; + Assert.True(double.IsNaN(MA_Series[0].v)); + Assert.True(double.IsNaN(MA_Series[8].v)); + Assert.False(double.IsNaN(MA_Series[9].v)); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Edge_numbers(Type classType) + { + TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity }; + var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; + Assert.Equal(4, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void handling_NaN(Type classType) + { + TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries; + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + public static IEnumerable MASeriesData() + { + foreach (var type in maSeriesTypes) + { + yield return new object[] { type }; + } + } +} +#nullable restore \ No newline at end of file diff --git a/archive/Tests/Basic tests/TBars_input.cs b/archive/Tests/Basic tests/TBars_input.cs new file mode 100644 index 00000000..02489ff7 --- /dev/null +++ b/archive/Tests/Basic tests/TBars_input.cs @@ -0,0 +1,97 @@ +using Xunit; +using System; +using System.Runtime.InteropServices; +using QuanTAlib; + +namespace Basics; +#nullable disable +public class TBars +{ + private static Type[] maSeriesTypes = new Type[] + { + typeof(ATR_Series), + typeof(ATRP_Series), + typeof(TR_Series), + typeof(ADL_Series), + typeof(CCI_Series), + typeof(OBV_Series), + typeof(ADOSC_Series), + typeof(MIDPRICE_Series), + }; + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Name_exists(Type classType) + { + GBM_Feed data = new(10); + + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + Assert.NotEmpty(MA_Series.Name); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Series_Length(Type classType) + { + GBM_Feed data = new(1000); + + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + Assert.Equal(1000, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Return_data(Type classType) + { + GBM_Feed data = new(10); + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + var result = MA_Series.Add((DateTime.Today, 1, 2, 3, 4, 5)); + Assert.Equal(result.v, MA_Series.Last.v); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Update(Type classType) + { + GBM_Feed data = new(10); + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + var pre_update = MA_Series.Last; + + var pre_data = data.Last; + data.Add((DateTime.Today, 1, 2, 3, 4, 5), true); + data.Add(pre_data, true); + + Assert.Equal(pre_update.v, MA_Series.Last.v); + Assert.Equal(data.Count, MA_Series.Count); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Reset(Type classType) + { + GBM_Feed data = new(10); + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + MA_Series.Reset(); + data.Add(); + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + [Theory] + [MemberData(nameof(MASeriesData))] + public void Period_default(Type classType) + { + GBM_Feed data = new(100); + + var MA_Series = Activator.CreateInstance(classType, data) as TSeries; + Assert.False(double.IsNaN(MA_Series.Last.v)); + } + + public static IEnumerable MASeriesData() + { + foreach (var type in maSeriesTypes) + { + yield return new object[] { type }; + } + } +} +#nullable restore \ No newline at end of file diff --git a/Tests/Pairs/ADD_Test.cs b/archive/Tests/Pairs/ADD_Test.cs similarity index 95% rename from Tests/Pairs/ADD_Test.cs rename to archive/Tests/Pairs/ADD_Test.cs index fa687709..2c86e5a8 100644 --- a/Tests/Pairs/ADD_Test.cs +++ b/archive/Tests/Pairs/ADD_Test.cs @@ -1,64 +1,64 @@ -using Xunit; -using System; -using QuanTAlib; - -namespace Pairs; -public class ADD_Test -{ - [Fact] - public void ADDSeriesSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - ADD_Series c = new(a, b); - Assert.Equal(5, c.Last().v); - } - - [Fact] - public void ADDSeriesDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - ADD_Series c = new(a, 10.0); - Assert.Equal(15, c.Last().v); - } - - [Fact] - public void ADDDoubleSeries_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - ADD_Series c = new(10.0, a); - Assert.Equal(15, c.Last().v); - } - - [Fact] - public void ADDEventing_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - ADD_Series c = new(a, b); - a.Add(2); - b.Add(2); - Assert.Equal(4, c.Last().v); - } - - [Fact] - public void ADDUpdateDouble_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - double b = 10; - ADD_Series c = new(a, b); - a.Add(0, true); - Assert.Equal(10, c.Last().v); - } - - [Fact] - public void ADDUpdating_Test() - { - TSeries a = new() { 0, 1, 2, 3, 4, 5 }; - TSeries b = new() { 5, 4, 3, 2, 1, 0 }; - ADD_Series c = new(a, b); - a.Add(10, true); - b.Add(10, true); - Assert.Equal(20, c.Last().v); - } -} +using Xunit; +using System; +using QuanTAlib; + +namespace Pairs; +public class ADD_Test +{ + [Fact] + public void ADDSeriesSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 0 }; + ADD_Series c = new(a, b); + Assert.Equal(5, c.Last().v); + } + + [Fact] + public void ADDSeriesDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + ADD_Series c = new(a, 10.0); + Assert.Equal(15, c.Last().v); + } + + [Fact] + public void ADDDoubleSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + ADD_Series c = new(10.0, a); + Assert.Equal(15, c.Last().v); + } + + [Fact] + public void ADDEventing_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 0 }; + ADD_Series c = new(a, b); + a.Add(2); + b.Add(2); + Assert.Equal(4, c.Last().v); + } + + [Fact] + public void ADDUpdateDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + double b = 10; + ADD_Series c = new(a, b); + a.Add(0, true); + Assert.Equal(10, c.Last().v); + } + + [Fact] + public void ADDUpdating_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 0 }; + ADD_Series c = new(a, b); + a.Add(10, true); + b.Add(10, true); + Assert.Equal(20, c.Last().v); + } +} diff --git a/archive/Tests/Pairs/DIV_Test.cs b/archive/Tests/Pairs/DIV_Test.cs new file mode 100644 index 00000000..0b011bc6 --- /dev/null +++ b/archive/Tests/Pairs/DIV_Test.cs @@ -0,0 +1,64 @@ +using Xunit; +using System; +using QuanTAlib; + +namespace Pairs; +public class DIV_Test +{ + [Fact] + public void DIVSeriesSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 15 }; + TSeries b = new() { 5, 4, 3, 2, 1, 3 }; + DIV_Series c = new(a, b); + Assert.Equal(5, c.Last().v); + } + + [Fact] + public void DIVSeriesDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; + DIV_Series c = new(a, 0); + Assert.Equal(double.PositiveInfinity, c.Last().v); + } + + [Fact] + public void DIVDoubleSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 3.0 }; + DIV_Series c = new(12.0, a); + Assert.Equal(4.0, c.Last().v); + } + + [Fact] + public void DIVEventing_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 0 }; + DIV_Series c = new(a, b); + a.Add(12.0); + b.Add(2); + Assert.Equal(6.0, c.Last().v); + } + + [Fact] + public void DIVUpdatewDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 15 }; + double b = 2; + DIV_Series c = new(a, b); + a.Add(10, true); + Assert.Equal(5, c.Last().v); + } + + [Fact] + public void DIVUpdating_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 1 }; + DIV_Series c = new(a, b); + a.Add(10, true); + b.Add(2, true); + Assert.Equal(5, c.Last().v); + } +} diff --git a/archive/Tests/Pairs/MUL_Test.cs b/archive/Tests/Pairs/MUL_Test.cs new file mode 100644 index 00000000..cc1c9718 --- /dev/null +++ b/archive/Tests/Pairs/MUL_Test.cs @@ -0,0 +1,64 @@ +using Xunit; +using System; +using QuanTAlib; + +namespace Pairs; +public class MUL_Test +{ + [Fact] + public void MULSeriesSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 1 }; + MUL_Series c = new(a, b); + Assert.Equal(5, c.Last().v); + } + + [Fact] + public void MULSeriesDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + MUL_Series c = new(a, 10.0); + Assert.Equal(50, c.Last().v); + } + + [Fact] + public void MULDoubleSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + MUL_Series c = new(5.0, a); + Assert.Equal(25, c.Last().v); + } + + [Fact] + public void MULEventing_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 0 }; + MUL_Series c = new(a, b); + a.Add(2); + b.Add(5); + Assert.Equal(10, c.Last().v); + } + + [Fact] + public void MULUpdateDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + double b = 10; + MUL_Series c = new(a, b); + a.Add(2, true); + Assert.Equal(20, c.Last().v); + } + + [Fact] + public void MULUpdating_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 0 }; + MUL_Series c = new(a, b); + a.Add(10, true); + b.Add(10, true); + Assert.Equal(100, c.Last().v); + } +} diff --git a/archive/Tests/Pairs/SUB_Test.cs b/archive/Tests/Pairs/SUB_Test.cs new file mode 100644 index 00000000..2a96d81d --- /dev/null +++ b/archive/Tests/Pairs/SUB_Test.cs @@ -0,0 +1,64 @@ +using Xunit; +using System; +using QuanTAlib; + +namespace Pairs; +public class SUB_Test +{ + [Fact] + public void SUBSeriesSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 1 }; + SUB_Series c = new(a, b); + Assert.Equal(4, c.Last().v); + } + + [Fact] + public void SUBSeriesDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; + SUB_Series c = new(a, 10.0); + Assert.Equal(5.0, c.Last().v); + } + + [Fact] + public void SUBDoubleSeries_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 15.0 }; + SUB_Series c = new(10.0, a); + Assert.Equal(-5.0, c.Last().v); + } + + [Fact] + public void SUBEventing_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 0 }; + SUB_Series c = new(a, b); + a.Add(7.0); + b.Add(2); + Assert.Equal(5.0, c.Last().v); + } + + [Fact] + public void SUBUpdatewDouble_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 15 }; + double b = 10; + SUB_Series c = new(a, b); + a.Add(1, true); + Assert.Equal(-9, c.Last().v); + } + + [Fact] + public void SUBUpdating_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + TSeries b = new() { 5, 4, 3, 2, 1, 1 }; + SUB_Series c = new(a, b); + a.Add(10, true); + b.Add(0, true); + Assert.Equal(10, c.Last().v); + } +} diff --git a/archive/Tests/Pairs/TBars_Test.cs b/archive/Tests/Pairs/TBars_Test.cs new file mode 100644 index 00000000..cae4ecec --- /dev/null +++ b/archive/Tests/Pairs/TBars_Test.cs @@ -0,0 +1,112 @@ +using Xunit; +using System; +using QuanTAlib; + +namespace Bars; +public class TBars_Test +{ + [Fact] + public void InsertingTuple() + { + TBars s = new() { (t: DateTime.Today, o: double.Epsilon, h: double.NaN, l: Double.MaxValue, c: Double.NegativeInfinity, v: Double.PositiveInfinity) }; + var tup = (t: DateTime.Today, o: double.Epsilon, h: double.NaN, l: Double.MaxValue, + c: Double.NegativeInfinity, v: Double.PositiveInfinity); + Assert.Equal(tup, s[^1]); + } + + [Fact] + public void Casting_Parameters() + { + TBars s = new() + { + { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1, false } + }; + Assert.Equal(0.1, s[^1].o); + Assert.Equal(1.1, s[^1].h); + Assert.Equal(2.1, s[^1].l); + Assert.Equal(3.1, s[^1].c); + Assert.Equal(4.1, s[^1].v); + Assert.Equal(DateTime.Today, s[^1].t); + Assert.Single(s); + } + + [Fact] + public void Updating_Value() + { + TBars s = new() + { + { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1 } + }; + s.Add(DateTime.Today, 1.0, 1.0, 1.0, 1.0, 1.0, update: false); + s.Add(DateTime.Today, 0.0, 0.0, 0.0, 0.0, 0.0, update: true); + Assert.Equal(0.0, s[^1].o); + Assert.Equal(0.0, s[^1].h); + Assert.Equal(0.0, s[^1].l); + Assert.Equal(0.0, s[^1].c); + Assert.Equal(0.0, s[^1].v); + Assert.Equal(2, s.Count); + } + [Fact] + public void Extracting_TSeries() + { + TBars s = new() + { + { DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1 }, + { DateTime.Today, 2.1, 3.1, 4.1, 5.1, 6.1 } + }; + + TSeries t = s.Open; + Assert.Equal(t.t, s.Open.t); + Assert.Equal(t.v, s.Open.v); + + t = s.High; + Assert.Equal(t.t, s.High.t); + Assert.Equal(t.v, s.High.v); + + t = s.Low; + Assert.Equal(t.t, s.Low.t); + Assert.Equal(t.v, s.Low.v); + + t = s.Close; + Assert.Equal(t.t, s.Close.t); + Assert.Equal(t.v, s.Close.v); + + t = s.Volume; + Assert.Equal(t.t, s.Volume.t); + Assert.Equal(t.v, s.Volume.v); + + t = s.HL2; + Assert.Equal(t.t, s.HL2.t); + Assert.Equal(t.v, s.HL2.v); + + t = s.OC2; + Assert.Equal(t.t, s.OC2.t); + Assert.Equal(t.v, s.OC2.v); + + t = s.OHL3; + Assert.Equal(t.t, s.OHL3.t); + Assert.Equal(t.v, s.OHL3.v); + + t = s.HLC3; + Assert.Equal(t.t, s.HLC3.t); + Assert.Equal(t.v, s.HLC3.v); + + t = s.OHLC4; + Assert.Equal(t.t, s.OHLC4.t); + Assert.Equal(t.v, s.OHLC4.v); + + t = s.HLCC4; + Assert.Equal(t.t, s.HLCC4.t); + Assert.Equal(t.v, s.HLCC4.v); + } + [Fact] + public void Broadcasting_Events() + { + TBars s = new() { (DateTime.Today, 2.1, 3.1, 4.1, 5.1, 6.1) }; + TSeries t = new(); + s.Close.Pub += t.Sub; + s.Add(DateTime.Today, 0.1, 1.1, 2.1, 3.1, 4.1, false); + Assert.Equal(s.Close.v, t.v); + Assert.Equal(s.Close.Count, t.Count); + } +} diff --git a/archive/Tests/Tests.csproj b/archive/Tests/Tests.csproj new file mode 100644 index 00000000..41673a8b --- /dev/null +++ b/archive/Tests/Tests.csproj @@ -0,0 +1,45 @@ + + + net7.0 + preview + enable + enable + false + AnyCPU;x64 + 0.2.1.0 + 0.2.1.0 + 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d + 0.2.1-dev.2 + $(NoWarn);NETSDK1057 + true + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/archive/Tests/Validations/Trends/Pandas_TA.cs b/archive/Tests/Validations/Trends/Pandas_TA.cs new file mode 100644 index 00000000..4656dc13 --- /dev/null +++ b/archive/Tests/Validations/Trends/Pandas_TA.cs @@ -0,0 +1,484 @@ +using Xunit; +using System; +using QuanTAlib; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Python.Runtime; + +namespace Validations; + +public class PandasTA : IDisposable +{ + private bool disposed = false; + private readonly GBM_Feed bars; + private readonly Random rnd = new(); + private readonly int period, skip; + private readonly int digits; + private readonly dynamic np; + private readonly dynamic ta; + private readonly dynamic pd; + private readonly dynamic df; + + public PandasTA() + { + bars = new GBM_Feed(5000, 0.8, 0.0); + period = rnd.Next(28) + 3; + skip = period + 50; + digits = 8; + + var pythonDLL = PythonLibrary.Locate(); + Runtime.PythonDLL = pythonDLL; + PythonEngine.Initialize(); + + np = Py.Import("numpy"); + pd = Py.Import("pandas"); + ta = Py.Import("pandas_ta"); + + string[] cols = { "open", "high", "low", "close", "volume" }; + var ary = new double[bars.Count, 5]; + for (var i = 0; i < bars.Count; i++) + { + ary[i, 0] = bars.Open[i].v; + ary[i, 1] = bars.High[i].v; + ary[i, 2] = bars.Low[i].v; + ary[i, 3] = bars.Close[i].v; + ary[i, 4] = bars.Volume[i].v; + } + + df = ta.DataFrame(data: np.array(ary), index: np.array(bars.Close.t), columns: np.array(cols)); + } + + public void Dispose() + { + Dispose(true); + PythonEngine.Shutdown(); + GC.SuppressFinalize(this); + } + + ~PandasTA() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + disposed = true; + } + } + + [Fact] + private void ADL() + { + ADL_Series QL = new(bars); + var pta = df.ta.ad(high: df.high, low: df.low, close: df.close, volume: df.volume); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void BBANDS() + { + BBANDS_Series QL = new(bars.Close, period); + var pta = df.ta.bbands(close: df.close, length: period).to_numpy(); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL.Lower[i].v; + var PanTA_item = (double)pta[i][0]; //lower + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + QL_item = QL.Mid[i].v; + PanTA_item = (double)pta[i][1]; //mid + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + QL_item = QL.Upper[i].v; + PanTA_item = (double)pta[i][2]; //upper + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void BIAS() + { + BIAS_Series QL = new(bars.Close, period, false); + var pta = df.ta.bias(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void CCI() + { + CCI_Series QL = new(bars, period, false); + var pta = df.ta.cci(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void DEMA() + { + DEMA_Series QL = new(bars.Close, period, false); + var pta = df.ta.dema(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void EMA() + { + EMA_Series QL = new(bars.Close, period, false); + var pta = df.ta.ema(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void ENTROPY() + { + ENTROPY_Series QL = new(bars.Close, period, false); + var pta = df.ta.entropy(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void HL2() + { + var pta = df.ta.hl2(high: df.high, low: df.low); + for (var i = bars.HL2.Length - 1; i > skip; i--) + { + var QL_item = bars.HL2[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void HLC3() + { + var pta = df.ta.hlc3(high: df.high, low: df.low, close: df.close); + for (var i = bars.HLC3.Length; i > skip; i--) + { + var QL_item = bars.HLC3[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void HMA() + { + HMA_Series QL = new(bars.Close, period, false); + var pta = df.ta.hma(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void KURTOSIS() + { + KURTOSIS_Series QL = new(bars.Close, period, false); + var pta = df.ta.kurtosis(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void MACD() + { + MACD_Series QL = new(bars.Close, 26, 12, 9, false); + var pta = df.ta.macd(close: df.close).to_numpy(); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1][0]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + QL_item = QL.Signal[i - 1].v; + PanTA_item = (double)pta[i - 1][2]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void MAD() + { + MAD_Series QL = new(bars.Close, period, false); + var pta = df.ta.mad(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void MEDIAN() + { + MEDIAN_Series QL = new(bars.Close, period); + var pta = df.ta.median(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void OBV() + { + OBV_Series QL = new(bars); + var pta = df.ta.obv(close: df.close, volume: df.volume); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void OHLC4() + { + var pta = df.ta.ohlc4(open: df.open, high: df.high, low: df.low, close: df.close); + for (var i = bars.OHLC4.Length; i > skip; i--) + { + var QL_item = bars.OHLC4[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void SDEV() + { + SDEV_Series QL = new(bars.Close, period, false); + var pta = df.ta.stdev(close: df.close, length: period, ddof: 0); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void SMA() + { + SMA_Series QL = new(bars.Close, period, false); + var pta = df.ta.sma(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void SSDEV() + { + SSDEV_Series QL = new(bars.Close, period, false); + var pta = df.ta.stdev(close: df.close, length: period, ddof: 1); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void SVARIANCE() + { + SVAR_Series QL = new(bars.Close, period); + var pta = df.ta.variance(close: df.close, length: period, ddof: 1); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void TEMA() + { + TEMA_Series QL = new(bars.Close, period, false); + var pta = df.ta.tema(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void TR() + { + TR_Series QL = new(bars); + var pta = df.ta.true_range(high: df.high, low: df.low, close: df.close); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void TRIMA() + { + // TODO: return length to variable length (period) when Pandas-TA fixes trima to calculate even periods right + TRIMA_Series QL = new(bars.Close, 11); + var pta = df.ta.trima(close: df.close, length: 11); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void VARIANCE() + { + VAR_Series QL = new(bars.Close, period); + var pta = df.ta.variance(close: df.close, length: period, ddof: 0); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void WMA() + { + WMA_Series QL = new(bars.Close, period, false); + var pta = df.ta.wma(close: df.close, length: period); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + private void ZSCORE() + { + ZSCORE_Series QL = new(bars.Close, period, false); + var pta = df.ta.zscore(close: df.close, length: period, ddof: 0); + for (var i = QL.Length - 1; i > skip; i--) + { + var QL_item = QL[i - 1].v; + var PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } +} + +public static class PythonLibrary +{ + public static string Locate() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? Array.Empty(); + foreach (string path in paths) + { + string[] pythonDLLs = Directory.GetFiles(path, "python3*.dll"); + if (pythonDLLs.Length > 0) + { + foreach (string item in pythonDLLs) + { + if (!item.EndsWith("python3.dll", StringComparison.OrdinalIgnoreCase)) + { + return item; + } + } + + } + } + throw new FileNotFoundException("Python library not found in PATH"); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return "/usr/lib/x86_64-linux-gnu/libpython3.10.so"; + /* + List pythonLibraries = new List(); + List directoriesToSearch = new List { "/home/runner/.local/lib" }; // Add more directories as needed + string filePattern = "libpython3.*.so"; + SearchFiles(directoriesToSearch, filePattern, pythonLibraries); + + if (pythonLibraries.Count > 0) { + return pythonLibraries[0]; + } + else { + throw new FileNotFoundException("Python library not found"); + } + */ + } + + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + throw new NotSupportedException("Not supported yet"); + } + + else { throw new NotSupportedException("Unsupported operating system"); } + } + static void SearchFiles(List directoriesToSearch, string filePattern, List foundFiles) + { + foreach (string directory in directoriesToSearch) + { + if (Directory.Exists(directory)) + { + try + { + string[] files = Directory.GetFiles(directory, filePattern, SearchOption.AllDirectories); + foundFiles.AddRange(files); + } + catch (Exception e) + { + Console.WriteLine("Error searching in directory: " + directory + " - " + e.Message); + } + } + } + } +} \ No newline at end of file diff --git a/archive/Tests/Validations/Trends/Skender.cs b/archive/Tests/Validations/Trends/Skender.cs new file mode 100644 index 00000000..393f55f1 --- /dev/null +++ b/archive/Tests/Validations/Trends/Skender.cs @@ -0,0 +1,489 @@ +using System; +using QuanTAlib; +using Skender.Stock.Indicators; +using Xunit; + +namespace Validations; +public class Skender +{ + private readonly GBM_Feed bars; + private readonly Random rnd = new(); + private readonly int period, digits, skip; + private readonly IEnumerable quotes; + + + public Skender() + { + bars = new(Bars: 10000, Volatility: 0.5, Drift: 0.0, Precision: 2); + period = rnd.Next(30) + 5; + digits = 6; //minimizing rounding errors in type conversions + skip = period + 2; + + quotes = bars.Select(q => new Quote + { + Date = q.t, + Open = (decimal)q.o, + High = (decimal)q.h, + Low = (decimal)q.l, + Close = (decimal)q.c, + Volume = (decimal)q.v + }); + } + + /* + [Fact] + public void ADL() + { + ADL_Series QL = new(bars); + var SK = quotes.GetAdl().Select(i => i.Adl); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1)!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); + } + } + */ + [Fact] + public void ALMA() + { + ALMA_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetAlma(period).Select(i => i.Alma.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void ATR() + { + ATR_Series QL = new(bars, period: period, useNaN: false); + var SK = quotes.GetAtr(period).Select(i => i.Atr.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void ATRP() + { + ATRP_Series QL = new(bars, period, false); + var SK = quotes.GetAtr(period).Select(i => i.Atrp.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void BBANDS() + { + BBANDS_Series QL = new(bars.Close, period, 2.0, useNaN: false); + var SK = quotes.GetBollingerBands(period, 2.0); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL.Mid[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Sma!.Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.Upper[i - 1].v; + SK_item = SK.ElementAt(i - 1).UpperBand!.Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.Lower[i - 1].v; + SK_item = SK.ElementAt(i - 1).LowerBand!.Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.Bandwidth[i - 1].v; + SK_item = SK.ElementAt(i - 1).Width!.Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.PercentB[i - 1].v; + SK_item = SK.ElementAt(i - 1).PercentB!.Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.Zscore[i - 1].v; + SK_item = SK.ElementAt(i - 1).ZScore!.Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void CCI() + { + CCI_Series QL = new(bars, period, false); + var SK = quotes.GetCci(period).Select(i => i.Cci.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void CMO() + { + CMO_Series QL = new(bars.Close, period, false); + var SK = quotes.GetCmo(period).Select(i => i.Cmo.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void CORR() + { + CORR_Series QL = new(bars.High, bars.Low, period, false); + var SK = quotes.Use(CandlePart.High).GetCorrelation(quotes.Use(CandlePart.Low), period).Select(i => i.Correlation.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void COVAR() + { + COVAR_Series QL = new(bars.High, bars.Low, period, false); + var SK = quotes.Use(CandlePart.High).GetCorrelation(quotes.Use(CandlePart.Low), period).Select(i => i.Covariance.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void DEMA() + { + DEMA_Series QL = new(bars.Close, period, false, useSMA: true); + var SK = quotes.GetDema(period).Select(i => i.Dema.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void EMA() + { + EMA_Series QL = new(bars.Close, period, false); + var SK = quotes.GetEma(lookbackPeriods: period).Select(i => i.Ema.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void HL2() + { + TSeries QL = bars.HL2; + var SK = quotes.GetBaseQuote(CandlePart.HL2).ToList(); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void HLC3() + { + TSeries QL = bars.HLC3; + var SK = quotes.GetBaseQuote(CandlePart.HLC3).ToList(); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void HMA() + { + HMA_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetHma(period).Select(i => i.Hma.Null2NaN()!); + for (int i = QL.Length; i > skip * 2; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + + [Fact] + public void KAMA() + { + // TODO: check precision of KAMA() + KAMA_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetKama(period).Select(i => i.Kama.Null2NaN()!); + for (int i = QL.Length; i > skip + 2; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void SLOPE() + { + SLOPE_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetSlope(period); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = (double)SK.ElementAt(i - 1).Slope!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.Intercept[i - 1].v; + SK_item = (double)SK.ElementAt(i - 1).Intercept!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.RSquared[i - 1].v; + SK_item = (double)SK.ElementAt(i - 1).RSquared!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.StdDev[i - 1].v; + SK_item = (double)SK.ElementAt(i - 1).StdDev!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void MACD() + { + MACD_Series QL = new(bars.Close, 26, 12, 9, useNaN: false); + var SK = quotes.GetMacd(12, 26, 9); + for (int i = QL.Length; i > 27; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Macd.Null2NaN()!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + //QL_item = QL.Signal[i - 1].v; + //SK_item = SK.ElementAt(i - 1).Signal.Null2NaN()!; + //Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); + } + } + [Fact] + public void MAD() + { + MAD_Series QL = new(bars.Close, period, false); + var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mad.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void MAMA() + { + MAMA_Series QL = new(bars.HL2, fastlimit: 0.5, slowlimit: 0.05); + var SK = quotes.GetMama(fastLimit: 0.5, slowLimit: 0.05); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Mama.Null2NaN()!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + QL_item = QL.Fama[i - 1].v; + SK_item = SK.ElementAt(i - 1).Fama.Null2NaN()!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void MAPE() + { + MAPE_Series QL = new(bars.Close, period, false); + var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mape.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void MSE() + { + MSE_Series QL = new(bars.Close, period, false); + var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mse.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void OBV() + { + OBV_Series QL = new(bars, period, false); + var SK = quotes.GetObv(period).Select(i => i.Obv!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL.Last().v; + // adding volume[0] to OBV to pass the test and keep compatibility with TA-LIB + double SK_item = SK.Last()! + (double)quotes.First().Volume!; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void OC2() + { + TSeries QL = bars.OC2; + var SK = quotes.GetBaseQuote(CandlePart.OC2).ToList(); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void OHL3() + { + TSeries QL = bars.OHL3; + var SK = quotes.GetBaseQuote(CandlePart.OHL3).ToList(); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void OHLC4() + { + TSeries QL = bars.OHLC4; + var SK = quotes.GetBaseQuote(CandlePart.OHLC4).ToList(); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1).Value; + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void RSI() + { + RSI_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetRsi(period).Select(i => i.Rsi.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void SDEV() + { + SDEV_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetStdDev(period).Select(i => i.StdDev.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void SMA() + { + SMA_Series QL = new(bars.Close, period, false); + var SK = quotes.GetSma(period).Select(i => i.Sma.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void SMMA() + { + SMMA_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetSmma(period).Select(i => i.Smma.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void T3() + { + T3_Series QL = new(source: bars.Close, period: period, vfactor: 0.7, false); + var SK = quotes.GetT3(lookbackPeriods: period, volumeFactor: 0.7).Select(i => i.T3.Null2NaN()!); + for (int i = QL.Length; i > period * 15; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void TRIX() + { + TRIX_Series QL = new(bars.Close, period, false); + var SK = quotes.GetTrix(period).Select(i => i.Trix.Null2NaN()!); + for (int i = QL.Length; i > period * 12; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void TEMA() + { + TEMA_Series QL = new(bars.Close, period, false); + var SK = quotes.GetTema(period).Select(i => i.Tema.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void TR() + { + TR_Series QL = new(bars); + var SK = quotes.GetTr().Select(i => i.Tr.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void WMA() + { + WMA_Series QL = new(bars.Close, period, false); + var SK = quotes.GetWma(period).Select(i => i.Wma.Null2NaN()!); + for (int i = QL.Length; i > skip * 2; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + [Fact] + public void ZSCORE() + { + ZSCORE_Series QL = new(bars.Close, period, useNaN: false); + var SK = quotes.GetStdDev(period).Select(i => i.ZScore.Null2NaN()!); + for (int i = QL.Length; i > skip; i--) + { + double QL_item = QL[i - 1].v; + double SK_item = SK.ElementAt(i - 1); + Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); + } + } + +} diff --git a/archive/Tests/Validations/Trends/TA_LIB.cs b/archive/Tests/Validations/Trends/TA_LIB.cs new file mode 100644 index 00000000..0e2c3654 --- /dev/null +++ b/archive/Tests/Validations/Trends/TA_LIB.cs @@ -0,0 +1,487 @@ +using Xunit; +using System; +using TALib; +using QuanTAlib; + +namespace Validations; +public class Ta_Lib +{ + private readonly GBM_Feed bars; + private readonly Random rnd = new(); + private readonly int period, digits, skip; + private readonly double[] TALIB; + private readonly double[] TALIB2; + private readonly double[] inopen; + private readonly double[] inhigh; + private readonly double[] inlow; + private readonly double[] inclose; + private readonly double[] involume; + + public Ta_Lib() + { + bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3); + period = rnd.Next(28) + 3; + skip = period + 2; + digits = 9; + + TALIB = new double[bars.Count]; + TALIB2 = new double[bars.Count]; + inopen = bars.Open.v.ToArray(); + inhigh = bars.High.v.ToArray(); + inlow = bars.Low.v.ToArray(); + inclose = bars.Close.v.ToArray(); + involume = bars.Volume.v.ToArray(); + } + + [Fact] + public void ADD() + { + ADD_Series QL = new(bars.Open, bars.Close); + Core.Add(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void ADL() + { + ADL_Series QL = new(bars); + Core.Ad(inhigh, inlow, inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > 0; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void ADOSC() + { + ADOSC_Series QL = new(bars, 3, 10, false); + Core.AdOsc(inhigh, inlow, inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip * 2; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void ATR() + { + ATR_Series QL = new(bars, period: period, useNaN: false); + Core.Atr(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + public void BBANDS() + { + double[] outMiddle = new double[bars.Count]; + double[] outUpper = new double[bars.Count]; + double[] outLower = new double[bars.Count]; + BBANDS_Series QL = new(bars.Close, period: period, multiplier: 2.0, false); + Core.Bbands(inclose, 0, bars.Count - 1, outRealUpperBand: outUpper, outRealMiddleBand: outMiddle, outRealLowerBand: outLower, out int outBegIdx, out _, optInTimePeriod: period, optInNbDevUp: 2.0, optInNbDevDn: 2.0); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL.Upper[i].v; + double TA_item = outUpper[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); + QL_item = QL.Mid[i].v; + TA_item = outMiddle[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); + QL_item = QL.Lower[i].v; + TA_item = outLower[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), high: Math.Exp(-digits)); + } + } + [Fact] + public void CCI() + { + CCI_Series QL = new(bars, period, false); + Core.Cci(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + /* CMO in TA-LIB is not valid + [Fact] + public void CMO() { + CMO_Series QL = new(bars.Close, period, false); + Core.Cmo(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + */ + [Fact] + public void CORR() + { + CORR_Series QL = new(bars.Open, bars.Close, period); + Core.Correl(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void DEMA() + { + DEMA_Series QL = new(bars.Close, period, false, useSMA: false); + Core.Dema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > period * 10; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void DIV() + { + DIV_Series QL = new(bars.Open, bars.Close); + Core.Div(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void EMA() + { + EMA_Series QL = new(bars.Close, period, false); + Core.Ema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void HL2() + { + TSeries QL = bars.HL2; + Core.MedPrice(inhigh, inlow, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void HLC3() + { + TSeries QL = bars.HLC3; + Core.TypPrice(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void HLCC4() + { + TSeries QL = bars.HLCC4; + Core.WclPrice(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void KAMA() + { + KAMA_Series QL = new(bars.Close, period, fast: 2, slow: 30); + Core.Kama(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outReal: TALIB, outBegIdx: out int outBegIdx, outNbElement: out _, optInTimePeriod: period); + for (int i = QL.Length - 1; i > skip * 15; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MACD() + { + double[] macdSignal = new double[bars.Count]; + double[] macdHist = new double[bars.Count]; + MACD_Series QL = new(bars.Close, slow: 26, fast: 12, signal: 9, false); + // TA-LIB runs EMA without SMA, leaving first 100 values for convergence + Core.Macd(inclose, 0, bars.Count - 1, outMacd: TALIB, outMacdSignal: macdSignal, outMacdHist: macdHist, out int outBegIdx, out _, optInFastPeriod: 12, optInSlowPeriod: 26, optInSignalPeriod: 9); + for (int i = QL.Length - 1; i > 100; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + QL_item = QL.Signal[i].v; + TA_item = macdSignal[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + /* + [Fact] + public void MAMA() + { + MAMA_Series QL = new(bars.Close, fastlimit: 0.5, slowlimit: 0.05); + Core.Mama(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outMama: TALIB, outFama: TALIB2, outBegIdx: out int outBegIdx, outNbElement: out _, optInFastLimit: 0.5, optInSlowLimit: 0.05); + for (int i = QL.Length - 1; i > skip * 10; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits-1), Math.Exp(-digits-1)); + } + } + */ + [Fact] + public void MAX() + { + MAX_Series QL = new(bars.Close, period, false); + Core.Max(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MIDPOINT() + { + MIDPOINT_Series QL = new(bars.Close, period, false); + Core.MidPoint(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MIDPRICE() + { + MIDPRICE_Series QL = new(bars, period, false); + Core.MidPrice(inhigh, inlow, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MIN() + { + MIN_Series QL = new(bars.Close, period, false); + Core.Min(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MUL() + { + MUL_Series QL = new(bars.Open, bars.Close); + Core.Mult(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void OBV() + { + OBV_Series QL = new(bars, period, false); + Core.Obv(inclose, involume, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void OHLC4() + { + TSeries QL = bars.OHLC4; + Core.AvgPrice(inopen, inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void RSI() + { + RSI_Series QL = new(bars.Close, period, false); + Core.Rsi(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SDEV() + { + SDEV_Series QL = new(bars.Close, period, false); + Core.StdDev(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SMA() + { + SMA_Series QL = new(bars.Close, period, false); + Core.Sma(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SUB() + { + SUB_Series QL = new(bars.Open, bars.Close); + Core.Sub(inopen, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SUM() + { + CUSUM_Series QL = new(bars.Close, period, false); + Core.Sum(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void T3() + { + T3_Series QL = new(source: bars.Close, period: period, vfactor: 0.7, useNaN: false); + Core.T3(inReal: inclose, startIdx: 0, endIdx: bars.Count - 1, outReal: TALIB, outBegIdx: out int outBegIdx, outNbElement: out _, optInTimePeriod: period, optInVFactor: 0.7); + for (int i = QL.Length - 1; i > period * 10; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void TEMA() + { + TEMA_Series QL = new(bars.Close, period, false); + Core.Tema(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip * 15; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void TR() + { + TR_Series QL = new(bars); + Core.TRange(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void TRIMA() + { + TRIMA_Series QL = new(bars.Close, period, false); + Core.Trima(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void TRIX() + { + TRIX_Series QL = new(bars.Close, period, useNaN: false, useSMA: true); + Core.Trix(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > period * 10; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void VAR() + { + VAR_Series QL = new(bars.Close, period, false); + Core.Var(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip * 15; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void WMA() + { + WMA_Series QL = new(bars.Close, period, false); + Core.Wma(inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TA_item = TALIB[i - outBegIdx]; + Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + +} diff --git a/archive/Tests/Validations/Trends/Tulip.cs b/archive/Tests/Validations/Trends/Tulip.cs new file mode 100644 index 00000000..fc36b359 --- /dev/null +++ b/archive/Tests/Validations/Trends/Tulip.cs @@ -0,0 +1,578 @@ +using Xunit; +using System; +using Tulip; +using QuanTAlib; + +namespace Validations; +public class Tulip_Test +{ + private readonly GBM_Feed bars; + private readonly Random rnd = new(); + private readonly int period, digits, skip; + private readonly double[] outdata; + private readonly double[] inopen; + private readonly double[] inhigh; + private readonly double[] inlow; + private readonly double[] inclose; + private readonly double[] involume; + + public Tulip_Test() + { + bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3); + period = rnd.Next(28) + 3; + skip = period + 5; + digits = 8; + + outdata = new double[bars.Count]; + inopen = bars.Open.v.ToArray(); + inhigh = bars.High.v.ToArray(); + inlow = bars.Low.v.ToArray(); + inclose = bars.Close.v.ToArray()!; + involume = bars.Volume.v.ToArray()!; + + } + [Fact] + public void ADL() + { + double[][] arrin = { inhigh, inlow, inclose, involume }; + double[][] arrout = { outdata }; + ADL_Series QL = new(bars); + Tulip.Indicators.ad.Run(inputs: arrin, options: new double[] { }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void ADD() + { + double[][] arrin = { inhigh, inlow }; + double[][] arrout = { outdata }; + ADD_Series QL = new(bars.High, bars.Low); + Tulip.Indicators.add.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void ADOSC() + { + double[][] arrin = { inhigh, inlow, inclose, involume }; + double[][] arrout = { outdata }; + int s = 3; + ADOSC_Series QL = new(bars, s, period, false); + Tulip.Indicators.adosc.Run(inputs: arrin, options: new double[] { s, period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void ATR() + { + double[][] arrin = { inhigh, inlow, inclose }; + double[][] arrout = { outdata }; + + ATR_Series QL = new(bars, period: period, useNaN: false); + Tulip.Indicators.atr.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + //Tulip ATR doesn't use warm-up SMA, compensating with 200 warming bars + for (int i = QL.Length - 1; i > 200 + skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void BBANDS() + { + double[][] arrin = { inclose }; + double[] outmid = new double[bars.Count]; + double[] outlower = new double[bars.Count]; + double[] outupper = new double[bars.Count]; + double[][] arrout = { outlower, outmid, outupper }; + BBANDS_Series QL = new(bars.Close, period, 2, false); + Tulip.Indicators.bbands.Run(inputs: arrin, options: new double[] { period, 2 }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL.Lower[i].v; + double TU_item = outlower[i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + QL_item = QL.Mid[i].v; + TU_item = outmid[i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + QL_item = QL.Upper[i].v; + TU_item = outupper[i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + /* + [Fact] + public void CCI() { + double[][] arrin = { inhigh, inlow, inclose }; + double[][] arrout = { outdata }; + CCI_Series QL = new(bars, period, useNaN: false); + Tulip.Indicators.cci.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) { + double QL_item = QL[i].v; + double TU_item = outdata[i - period + 1]; + Assert.Equal(QL_item,TU_item); + //Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + */ + [Fact] + public void CMO() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + CMO_Series QL = new(bars.Close, period, useNaN: false); + Tulip.Indicators.cmo.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void DECAY() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + DECAY_Series QL = new(bars.Close, period, useNaN: false); + Tulip.Indicators.decay.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip + 200; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void DEMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + DEMA_Series QL = new(bars.Close, period, useNaN: false, useSMA: false); + Tulip.Indicators.dema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip + 200; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - (period + period - 2)]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void DIV() + { + double[][] arrin = { inhigh, inlow }; + double[][] arrout = { outdata }; + DIV_Series QL = new(bars.High, bars.Low); + Tulip.Indicators.div.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void EDECAY() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + DECAY_Series QL = new(bars.Close, period, exponential: true, useNaN: false); + Tulip.Indicators.edecay.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip + 200; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void EMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + // Tulip EMA doesn't use SMA to warm-up + EMA_Series QL = new(bars.Close, period, false, useSMA: false); + Tulip.Indicators.ema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void HL2() + { + double[][] arrin = { inhigh, inlow }; + double[][] arrout = { outdata }; + + TSeries QL = bars.HL2; + Tulip.Indicators.medprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void HLC3() + { + double[][] arrin = { inhigh, inlow, inclose }; + double[][] arrout = { outdata }; + + TSeries QL = bars.HLC3; + Tulip.Indicators.typprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void HLCC4() + { + double[][] arrin = { inhigh, inlow, inclose }; + double[][] arrout = { outdata }; + + TSeries QL = bars.HLCC4; + Tulip.Indicators.wcprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + public void HMA() + { + int p = 10; + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + HMA_Series QL = new(bars.Close, p, false); + Tulip.Indicators.hma.Run(inputs: arrin, options: new double[] { p }, outputs: arrout); + for (int i = QL.Length - 1; i > skip + 2; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - p - 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits - 2), Math.Exp(-digits - 2)); + } + } + + [Fact] + public void KAMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + KAMA_Series QL = new(bars.Close, period); + Tulip.Indicators.kama.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > 250; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + + [Fact] + public void LINREG() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + SLOPE_Series QL = new(bars.Close, period); + Tulip.Indicators.linregslope.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MACD() + { + + double[] outsignal = new double[bars.Count]; + double[] outhist = new double[bars.Count]; + double[][] arrin = { inclose }; + double[][] arrout = { outdata, outsignal, outhist }; + MACD_Series QL = new(bars.Close, slow: 26, fast: 10, signal: 9); + Tulip.Indicators.macd.Run(inputs: arrin, options: new double[] { 10, 26, 9 }, outputs: arrout); + for (int i = QL.Length - 1; i > 150; i--) + { + double QL_item = QL[i].v; + double TU_item = outdata[i - 26 + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MAX() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + MAX_Series QL = new(bars.Close, period, false); + Tulip.Indicators.max.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MIN() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + MIN_Series QL = new(bars.Close, period, false); + Tulip.Indicators.min.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void MUL() + { + double[][] arrin = { inhigh, inlow }; + double[][] arrout = { outdata }; + MUL_Series QL = new(bars.High, bars.Low); + Tulip.Indicators.mul.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void OBV() + { + double[][] arrin = { inclose, involume }; + double[][] arrout = { outdata }; + OBV_Series QL = new(bars, period, false); + Tulip.Indicators.obv.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i] + arrin[1][0]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void OHLC4() + { + double[][] arrin = { inopen, inhigh, inlow, inclose }; + double[][] arrout = { outdata }; + + TSeries QL = bars.OHLC4; + Tulip.Indicators.avgprice.Run(inputs: arrin, options: new double[] { }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void RMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + RMA_Series QL = new(bars.Close, period, false); + Tulip.Indicators.wilders.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void RSI() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + RSI_Series QL = new(bars.Close, period, false); + Tulip.Indicators.rsi.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + SMA_Series QL = new(bars.Close, period, false); + Tulip.Indicators.sma.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SDEV() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + SDEV_Series QL = new(bars.Close, period, false); + Tulip.Indicators.stddev.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SUB() + { + double[][] arrin = { inhigh, inlow }; + double[][] arrout = { outdata }; + SUB_Series QL = new(bars.High, bars.Low); + Tulip.Indicators.sub.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void SUM() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + CUSUM_Series QL = new(bars.Close, period, false); + Tulip.Indicators.sum.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void TR() + { + double[][] arrin = { inhigh, inlow, inclose }; + double[][] arrout = { outdata }; + TR_Series QL = new(bars); + Tulip.Indicators.tr.Run(inputs: arrin, options: new double[] { }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void TEMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + TEMA_Series QL = new(bars.Close, period, false); + Tulip.Indicators.tema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip + 200; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - (period - 1) * 3]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void TRIMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + TRIMA_Series QL = new(bars.Close, period, false); + Tulip.Indicators.trima.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + /* + [Fact] + public void TRIX() { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + TRIX_Series QL = new(bars.Close, period); + Tulip.Indicators.trix.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > period+200; i--) { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - (period*3) + 2]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits+2), Math.Exp(-digits+2)); + } + } + */ + [Fact] + public void VAR() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + VAR_Series QL = new(bars.Close, period, false); + Tulip.Indicators.var.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void WMA() + { + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + WMA_Series QL = new(bars.Close, period, false); + Tulip.Indicators.wma.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + for (int i = QL.Length - 1; i > skip; i--) + { + double QL_item = QL[i].v; + double TU_item = arrout[0][i - period + 1]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + } + [Fact] + public void ZLEMA() + { + int p = 4; + double[][] arrin = { inclose }; + double[][] arrout = { outdata }; + ZLEMA_Series QL = new(bars.Close, p, false); + Tulip.Indicators.zlema.Run(inputs: arrin, options: new double[] { p }, outputs: arrout); + for (int i = QL.Length - 1; i > skip + 20; i--) + { + double QL_item = QL[i].v; + double TU_item = outdata[i]; + Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits - 2), Math.Exp(-digits - 2)); + } + } +} diff --git a/Tests/requirements.txt b/archive/Tests/requirements.txt similarity index 100% rename from Tests/requirements.txt rename to archive/Tests/requirements.txt diff --git a/archive/docs/.nojekyll b/archive/docs/.nojekyll new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/archive/docs/.nojekyll @@ -0,0 +1 @@ + diff --git a/docs/ALMA.md b/archive/docs/ALMA.md similarity index 100% rename from docs/ALMA.md rename to archive/docs/ALMA.md diff --git a/docs/DEMA.md b/archive/docs/DEMA.md similarity index 100% rename from docs/DEMA.md rename to archive/docs/DEMA.md diff --git a/docs/DWMA.md b/archive/docs/DWMA.md similarity index 100% rename from docs/DWMA.md rename to archive/docs/DWMA.md diff --git a/docs/EMA.md b/archive/docs/EMA.md similarity index 98% rename from docs/EMA.md rename to archive/docs/EMA.md index 8c0c934f..ba8091d8 100644 --- a/docs/EMA.md +++ b/archive/docs/EMA.md @@ -1,50 +1,50 @@ -# EMA: Exponential Moving Average - -Also known as exponentially weighted moving average, as it places greater weight on the most recent data points. -EMA reacts more agressively to recent data changes and calculates the current value using just the previous EMA value and current data point. The weight applied to the new value is typically $k = 2 / (period-1)$ - -## Calculation - -EMA is a rolling calculation requiring only one historical data point to calculate the current value and is denoted as ${EMA}_{p}{(data)}$ where $p$ represents the period and $data$ represents the list of data points. - -Some implementations of EMA calculate a seeding value of $EMA$ as a ${SMA}_{p}$ when $n < period$ - and start the $EMA$ calculation only after the warm-up period. QuanTAlib offers an option to enable/disable SMA warm-up. - -$$ -EMA_n = \left\{ \begin{array}{cl} -\frac{1}{p}\left( data_{n}-data_{n-p}\right)+SMA_{n-1} & : \ n \leq period \\ -{k}\times ({data_{n}} - EMA_{n-1}) + EMA_{n-1} & : \ n > period -\end{array} \right. -$$ - -## Behavior -![Alt text](./img/EMA_chart.svg) - -## Reference Calculation -period = 5 -``` -TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; -EMA_Series ema = new(data, 5, useNaN: false); -EMA_Series ema_nan = new(data, 5, useNaN: true); -for (int i=0; i< data.Count; i++) - Console.WriteLine($"{i}\t{data[i].v,7:f2}\t{ema_nan[i].v,7:f3}\t{ema[i].v,7:f3}"); -``` -| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ | -|--|:--:|:--:|:--:|:--:|:--:|:--:| -|0| 81.59| **81.590**| _NaN_| _NaN_| _NaN_| _NaN_| -|1| 81.06| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_| -|2| 82.87| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_| -|3| 83.00| **82.130**| _NaN_| _NaN_| _NaN_| _NaN_| -|4| 83.61| **82.426**| _82.426_| _82.426_| _82.426_| _82.426_| -|5| 83.15| **82.667**| _82.667_| _82.667_| _82.667_|_82.667_| -|6| 82.84| **82.725**| _82.725_| _82.725_| _82.725_|_82.725_| -|7| 83.99| **83.147**| _83.147_| _83.147_| _83.147_|_83.147_| -|8| 84.55| **83.614**| _83.614_| _83.614_| _83.614_|_83.614_| -|9| 84.36| **83.863**| _83.863_| _83.863_| _83.863_|_83.863_| -|10| 85.53| **84.419**| _84.419_| _84.419_| _84.419_|_84.419_| -|11| 86.54| **85.126**| _85.126_| _85.126_| _85.126_|_85.126_| -|12| 86.89| **85.714**| _85.714_| _85.714_| _85.714_|_85.714_| -|13| 87.77| **86.399**| _86.399_| _86.399_| _86.399_|_86.399_| -|14| 87.29| **86.696**| _86.696_| _86.696_| _86.696_|_86.696_| -## References - +# EMA: Exponential Moving Average + +Also known as exponentially weighted moving average, as it places greater weight on the most recent data points. +EMA reacts more agressively to recent data changes and calculates the current value using just the previous EMA value and current data point. The weight applied to the new value is typically $k = 2 / (period-1)$ + +## Calculation + +EMA is a rolling calculation requiring only one historical data point to calculate the current value and is denoted as ${EMA}_{p}{(data)}$ where $p$ represents the period and $data$ represents the list of data points. + +Some implementations of EMA calculate a seeding value of $EMA$ as a ${SMA}_{p}$ when $n < period$ - and start the $EMA$ calculation only after the warm-up period. QuanTAlib offers an option to enable/disable SMA warm-up. + +$$ +EMA_n = \left\{ \begin{array}{cl} +\frac{1}{p}\left( data_{n}-data_{n-p}\right)+SMA_{n-1} & : \ n \leq period \\ +{k}\times ({data_{n}} - EMA_{n-1}) + EMA_{n-1} & : \ n > period +\end{array} \right. +$$ + +## Behavior +![Alt text](./img/EMA_chart.svg) + +## Reference Calculation +period = 5 +``` +TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; +EMA_Series ema = new(data, 5, useNaN: false); +EMA_Series ema_nan = new(data, 5, useNaN: true); +for (int i=0; i< data.Count; i++) + Console.WriteLine($"{i}\t{data[i].v,7:f2}\t{ema_nan[i].v,7:f3}\t{ema[i].v,7:f3}"); +``` +| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ | +|--|:--:|:--:|:--:|:--:|:--:|:--:| +|0| 81.59| **81.590**| _NaN_| _NaN_| _NaN_| _NaN_| +|1| 81.06| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_| +|2| 82.87| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_| +|3| 83.00| **82.130**| _NaN_| _NaN_| _NaN_| _NaN_| +|4| 83.61| **82.426**| _82.426_| _82.426_| _82.426_| _82.426_| +|5| 83.15| **82.667**| _82.667_| _82.667_| _82.667_|_82.667_| +|6| 82.84| **82.725**| _82.725_| _82.725_| _82.725_|_82.725_| +|7| 83.99| **83.147**| _83.147_| _83.147_| _83.147_|_83.147_| +|8| 84.55| **83.614**| _83.614_| _83.614_| _83.614_|_83.614_| +|9| 84.36| **83.863**| _83.863_| _83.863_| _83.863_|_83.863_| +|10| 85.53| **84.419**| _84.419_| _84.419_| _84.419_|_84.419_| +|11| 86.54| **85.126**| _85.126_| _85.126_| _85.126_|_85.126_| +|12| 86.89| **85.714**| _85.714_| _85.714_| _85.714_|_85.714_| +|13| 87.77| **86.399**| _86.399_| _86.399_| _86.399_|_86.399_| +|14| 87.29| **86.696**| _86.696_| _86.696_| _86.696_|_86.696_| +## References + - https://en.wikipedia.org/wiki/Exponential_smoothing \ No newline at end of file diff --git a/docs/FMA.md b/archive/docs/FMA.md similarity index 100% rename from docs/FMA.md rename to archive/docs/FMA.md diff --git a/docs/HEMA.md b/archive/docs/HEMA.md similarity index 100% rename from docs/HEMA.md rename to archive/docs/HEMA.md diff --git a/docs/HMA.md b/archive/docs/HMA.md similarity index 100% rename from docs/HMA.md rename to archive/docs/HMA.md diff --git a/docs/HWMA.md b/archive/docs/HWMA.md similarity index 100% rename from docs/HWMA.md rename to archive/docs/HWMA.md diff --git a/docs/JMA.md b/archive/docs/JMA.md similarity index 100% rename from docs/JMA.md rename to archive/docs/JMA.md diff --git a/docs/KAMA.md b/archive/docs/KAMA.md similarity index 100% rename from docs/KAMA.md rename to archive/docs/KAMA.md diff --git a/docs/MAMA.md b/archive/docs/MAMA.md similarity index 100% rename from docs/MAMA.md rename to archive/docs/MAMA.md diff --git a/docs/QA.md b/archive/docs/QA.md similarity index 100% rename from docs/QA.md rename to archive/docs/QA.md diff --git a/docs/RMA.md b/archive/docs/RMA.md similarity index 100% rename from docs/RMA.md rename to archive/docs/RMA.md diff --git a/docs/SMA.md b/archive/docs/SMA.md similarity index 98% rename from docs/SMA.md rename to archive/docs/SMA.md index df069915..c84295b5 100644 --- a/docs/SMA.md +++ b/archive/docs/SMA.md @@ -1,57 +1,57 @@ -# SMA: Simple Moving Average - -SMA is is an arithmetic moving average where the weights in SMA are **equally** distributed across the given period, resulting in a mean() of the data within the period. - -## Calculation - -SMA is a rolling calculation that is looking backwards from the position ${n}$ and is denoted as ${SMA}_{p}{(data)}$ where $p$ represents the period and $data$ represents the list of data points: -$$ -SMA_p{(data)} = \frac{1}{p}\sum_{i=n-p+1}^{n} data_i -$$ -When calculating the value of the next $SMA_{p,next}$ while knowing previous SMA values, SMA calculation can be reduced to: -$$ -SMA_{p,next} = SMA_{p,prev}+\frac{1}{p}\left( data_{n+1}-data_{n+1-p}\right) -$$ - -## Implementation -`TSeries SMA_Series (TSeries source, int period = 0, bool useNaN = false)` - -- SMA_Series returns TSeries list -- `source`: input of type TSeries; SMA_Series automatically subscribes to events of new data added to the source -- `period`: optional size of a lookback window; if set to 0, SMA calculates cumulative average across the whole source -- `useNaN`: if set to _true_, SMA_Series will hide values within the initial period with NaN (for compatibility with other libraries) - -## Behavior -![Alt text](./img/SMA_chart.svg) -## Reference Calculation & Validation -period = 5 -``` -TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; -SMA_Series sma = new(data, 5, useNaN: false); -``` - -| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ | -|--|:--:|:--:|:--:|:--:|:--:|:--:| -| 0| 212.80|**212.80**| _NaN_| _NaN_| _NaN_| _NaN_| -| 1| 214.06|**213.43**| _NaN_| _NaN_| _NaN_| _NaN_| -| 2| 213.89|**213.58**| _NaN_| _NaN_| _NaN_| _NaN_| -| 3| 214.66|**213.85**| _NaN_| _NaN_| _NaN_| _NaN_| -| 4| 213.95|**213.87**| _213.87_| _213.87_| _213.87_| _213.87_| -| 5| 213.95|**214.10**| _214.10_| _214.10_| _214.10_| _214.10_| -| 6| 214.55|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| -| 7| 214.02|**214.23**| _214.23_| _214.23_| _214.23_| _214.23_| -| 8| 214.51|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| -| 9| 213.75|**214.16**| _214.16_| _214.16_| _214.16_| _214.16_| -|10| 214.22|**214.21**| _214.21_| _214.21_| _214.21_| _214.21_| -|11| 213.43|**213.99**| _213.99_| _213.99_| _213.99_| _213.99_| -|12| 214.21|**214.02**| _214.02_| _214.02_| _214.02_| _214.02_| -|13| 213.66|**213.85**| _213.85_| _213.85_| _213.85_| _213.85_| -|14| 215.03|**214.11**| _214.11_| _214.11_| _214.11_| _214.11_| -|15| 216.89|**214.64**| _214.64_| _214.64_| _214.64_| _214.64_| -|16| 216.66|**215.29**| _215.29_| _215.29_| _215.29_| _215.29_| - - -## References - - https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average - - Kaufman, Perry J. (2013) Trading Systems and Methods +# SMA: Simple Moving Average + +SMA is is an arithmetic moving average where the weights in SMA are **equally** distributed across the given period, resulting in a mean() of the data within the period. + +## Calculation + +SMA is a rolling calculation that is looking backwards from the position ${n}$ and is denoted as ${SMA}_{p}{(data)}$ where $p$ represents the period and $data$ represents the list of data points: +$$ +SMA_p{(data)} = \frac{1}{p}\sum_{i=n-p+1}^{n} data_i +$$ +When calculating the value of the next $SMA_{p,next}$ while knowing previous SMA values, SMA calculation can be reduced to: +$$ +SMA_{p,next} = SMA_{p,prev}+\frac{1}{p}\left( data_{n+1}-data_{n+1-p}\right) +$$ + +## Implementation +`TSeries SMA_Series (TSeries source, int period = 0, bool useNaN = false)` + +- SMA_Series returns TSeries list +- `source`: input of type TSeries; SMA_Series automatically subscribes to events of new data added to the source +- `period`: optional size of a lookback window; if set to 0, SMA calculates cumulative average across the whole source +- `useNaN`: if set to _true_, SMA_Series will hide values within the initial period with NaN (for compatibility with other libraries) + +## Behavior +![Alt text](./img/SMA_chart.svg) +## Reference Calculation & Validation +period = 5 +``` +TSeries data = new() {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29}; +SMA_Series sma = new(data, 5, useNaN: false); +``` + +| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ | +|--|:--:|:--:|:--:|:--:|:--:|:--:| +| 0| 212.80|**212.80**| _NaN_| _NaN_| _NaN_| _NaN_| +| 1| 214.06|**213.43**| _NaN_| _NaN_| _NaN_| _NaN_| +| 2| 213.89|**213.58**| _NaN_| _NaN_| _NaN_| _NaN_| +| 3| 214.66|**213.85**| _NaN_| _NaN_| _NaN_| _NaN_| +| 4| 213.95|**213.87**| _213.87_| _213.87_| _213.87_| _213.87_| +| 5| 213.95|**214.10**| _214.10_| _214.10_| _214.10_| _214.10_| +| 6| 214.55|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| +| 7| 214.02|**214.23**| _214.23_| _214.23_| _214.23_| _214.23_| +| 8| 214.51|**214.20**| _214.20_| _214.20_| _214.20_| _214.20_| +| 9| 213.75|**214.16**| _214.16_| _214.16_| _214.16_| _214.16_| +|10| 214.22|**214.21**| _214.21_| _214.21_| _214.21_| _214.21_| +|11| 213.43|**213.99**| _213.99_| _213.99_| _213.99_| _213.99_| +|12| 214.21|**214.02**| _214.02_| _214.02_| _214.02_| _214.02_| +|13| 213.66|**213.85**| _213.85_| _213.85_| _213.85_| _213.85_| +|14| 215.03|**214.11**| _214.11_| _214.11_| _214.11_| _214.11_| +|15| 216.89|**214.64**| _214.64_| _214.64_| _214.64_| _214.64_| +|16| 216.66|**215.29**| _215.29_| _215.29_| _215.29_| _215.29_| + + +## References + - https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average + - Kaufman, Perry J. (2013) Trading Systems and Methods - Murphy, J. (1999) Technical Analysis of the Financial Markets \ No newline at end of file diff --git a/docs/SMMA.md b/archive/docs/SMMA.md similarity index 100% rename from docs/SMMA.md rename to archive/docs/SMMA.md diff --git a/docs/T3.md b/archive/docs/T3.md similarity index 100% rename from docs/T3.md rename to archive/docs/T3.md diff --git a/docs/TEMA.md b/archive/docs/TEMA.md similarity index 100% rename from docs/TEMA.md rename to archive/docs/TEMA.md diff --git a/docs/TRIMA.md b/archive/docs/TRIMA.md similarity index 100% rename from docs/TRIMA.md rename to archive/docs/TRIMA.md diff --git a/docs/Trading_example.ipynb b/archive/docs/Trading_example.ipynb similarity index 100% rename from docs/Trading_example.ipynb rename to archive/docs/Trading_example.ipynb diff --git a/docs/WMA.md b/archive/docs/WMA.md similarity index 100% rename from docs/WMA.md rename to archive/docs/WMA.md diff --git a/docs/ZLEMA.md b/archive/docs/ZLEMA.md similarity index 100% rename from docs/ZLEMA.md rename to archive/docs/ZLEMA.md diff --git a/archive/docs/_sidebar.md b/archive/docs/_sidebar.md new file mode 100644 index 00000000..ceb3a715 --- /dev/null +++ b/archive/docs/_sidebar.md @@ -0,0 +1,24 @@ +* [Home](/) + * [FAQ - Frequently asked questions answered](QA.md) + +* [List of all Indicators](indicators.md "Indicators coverage") + + * [SMA - Simple Moving Average](SMA.md) + * [EMA - Exponential Moving Average](EMA.md) + * [WMA - Weighted Moving Average](WMA.md) + * [T3 - Tillson T3 Exponential MA](T3.md) + * [SMMA - Smoothed Moving Average](SMMA.md) + * [TRIMA - Triangular Moving Average](TRIMA.md) + * [DWMA - Double Weighted Moving Average](DWMA.md) + * [FMA - Fibonacci Moving Average](FMA.md) + * [DEMA - Double Exponential MA](DEMA.md) + * [TEMA - Triple Exponential MA](TEMA.md) + * [ALMA - Arnaud Legoux Moving Average](ALMA.md) + * [HMA - Hull Moving Average](HMA.md) + * [HEMA - Hull/Exponential Moving Average](HEMA.md) + * [HWMA - Holt-Winter Moving Average](HWMA.md) + * [MAMA - MESA Adaptive Moving Average](MAMA.md) + * [KAMA - Kaufman Adaptive Moving Average](KAMA.md) + * [ZLEMA - Zero-Lag Exponential MA](ZLEMA.md) + * [JMA - Jurik Moving Average](JMA.md) + diff --git a/docs/getting_started.ipynb b/archive/docs/getting_started.ipynb similarity index 100% rename from docs/getting_started.ipynb rename to archive/docs/getting_started.ipynb diff --git a/docs/img/ALMA_chart.svg b/archive/docs/img/ALMA_chart.svg similarity index 100% rename from docs/img/ALMA_chart.svg rename to archive/docs/img/ALMA_chart.svg diff --git a/docs/img/DEMA_chart.svg b/archive/docs/img/DEMA_chart.svg similarity index 100% rename from docs/img/DEMA_chart.svg rename to archive/docs/img/DEMA_chart.svg diff --git a/docs/img/DWMA_chart.svg b/archive/docs/img/DWMA_chart.svg similarity index 100% rename from docs/img/DWMA_chart.svg rename to archive/docs/img/DWMA_chart.svg diff --git a/docs/img/EMA_chart.svg b/archive/docs/img/EMA_chart.svg similarity index 100% rename from docs/img/EMA_chart.svg rename to archive/docs/img/EMA_chart.svg diff --git a/docs/img/FMA_chart.svg b/archive/docs/img/FMA_chart.svg similarity index 100% rename from docs/img/FMA_chart.svg rename to archive/docs/img/FMA_chart.svg diff --git a/docs/img/HEMA_chart.svg b/archive/docs/img/HEMA_chart.svg similarity index 100% rename from docs/img/HEMA_chart.svg rename to archive/docs/img/HEMA_chart.svg diff --git a/docs/img/HMA_chart.svg b/archive/docs/img/HMA_chart.svg similarity index 100% rename from docs/img/HMA_chart.svg rename to archive/docs/img/HMA_chart.svg diff --git a/docs/img/HWMA_chart.svg b/archive/docs/img/HWMA_chart.svg similarity index 100% rename from docs/img/HWMA_chart.svg rename to archive/docs/img/HWMA_chart.svg diff --git a/docs/img/JMA_chart.svg b/archive/docs/img/JMA_chart.svg similarity index 100% rename from docs/img/JMA_chart.svg rename to archive/docs/img/JMA_chart.svg diff --git a/docs/img/KAMA_chart.svg b/archive/docs/img/KAMA_chart.svg similarity index 100% rename from docs/img/KAMA_chart.svg rename to archive/docs/img/KAMA_chart.svg diff --git a/docs/img/MAMA_chart.svg b/archive/docs/img/MAMA_chart.svg similarity index 100% rename from docs/img/MAMA_chart.svg rename to archive/docs/img/MAMA_chart.svg diff --git a/docs/img/RMA_chart.svg b/archive/docs/img/RMA_chart.svg similarity index 100% rename from docs/img/RMA_chart.svg rename to archive/docs/img/RMA_chart.svg diff --git a/docs/img/SMA_chart.svg b/archive/docs/img/SMA_chart.svg similarity index 100% rename from docs/img/SMA_chart.svg rename to archive/docs/img/SMA_chart.svg diff --git a/docs/img/SMMA_chart.svg b/archive/docs/img/SMMA_chart.svg similarity index 100% rename from docs/img/SMMA_chart.svg rename to archive/docs/img/SMMA_chart.svg diff --git a/docs/img/T3_chart.svg b/archive/docs/img/T3_chart.svg similarity index 100% rename from docs/img/T3_chart.svg rename to archive/docs/img/T3_chart.svg diff --git a/docs/img/TEMA_chart.svg b/archive/docs/img/TEMA_chart.svg similarity index 100% rename from docs/img/TEMA_chart.svg rename to archive/docs/img/TEMA_chart.svg diff --git a/docs/img/TRIMA_chart.svg b/archive/docs/img/TRIMA_chart.svg similarity index 100% rename from docs/img/TRIMA_chart.svg rename to archive/docs/img/TRIMA_chart.svg diff --git a/docs/img/WMA_chart.svg b/archive/docs/img/WMA_chart.svg similarity index 100% rename from docs/img/WMA_chart.svg rename to archive/docs/img/WMA_chart.svg diff --git a/docs/img/ZLEMA_chart.svg b/archive/docs/img/ZLEMA_chart.svg similarity index 100% rename from docs/img/ZLEMA_chart.svg rename to archive/docs/img/ZLEMA_chart.svg diff --git a/archive/docs/img/quotes.gif b/archive/docs/img/quotes.gif new file mode 100644 index 00000000..f7ffdd1b Binary files /dev/null and b/archive/docs/img/quotes.gif differ diff --git a/archive/docs/index.html b/archive/docs/index.html new file mode 100644 index 00000000..e6588dd8 --- /dev/null +++ b/archive/docs/index.html @@ -0,0 +1,34 @@ + + + + + Document + + + + + + + +
+ + + + + + + + + diff --git a/docs/indicators.md b/archive/docs/indicators.md similarity index 98% rename from docs/indicators.md rename to archive/docs/indicators.md index c1545a8d..4c5301fe 100644 --- a/docs/indicators.md +++ b/archive/docs/indicators.md @@ -1,175 +1,175 @@ -# Coverage - -⭐= Calculation is validated against several TA libraries - -✔️= Validation tests passed - -❌= Issue - -|**BASIC TRANSFORMS**|**QuanTAlib**|**TA-LIB**|**Skender**|**Pandas TA**|**Tulip**| -|--|:--:|:--:|:--:|:--:|:--:| -|OC2 - (Open+Close)/2|️ `.OC2`||✔️CandlePart.OC2|| -|⭐HL2 - Median Price|`.HL2`|✔️MEDPRICE|✔️CandlePart.HL2|✔️hl2|✔️medprice| -|⭐HLC3 - Typical Price|`.HLC3`|✔️TYPPRICE|✔️CandlePart.HLC3|✔️hlc3|✔️typprice| -|OHL3 - (Open+High+Low)/3|`.OHL3`||✔️CandlePart.OHL3|| -|⭐OHLC4 - Average Price|`.OHLC4`|✔️AVGPRICE|️✔️CandlePart.OHLC4|✔️ohlc4|✔️avgprice| -|HLCC4 - Weighted Price|`.HLCC4`|✔️WCLPRICE|||✔️wcprice| -|MIDPOINT - Midpoint value|`MIDPOINT_Series`|✔️MIDPOINT||midpoint| -|MIDPRICE - Midpoint price|`MIDPRICE_Series`|✔️MIDPRICE||midprice| -|MAX - Max value|`MAX_Series`|✔️MAX|||✔️max| -|MIN - Min value|`MIN_Series`|✔️MIN|||✔️min| -|SUM - Summation|`SUM_Series`|✔️SUM|||✔️sum| -|ADD - Addition|`ADD_Series`|✔️ADD|||✔️add| -|SUB - Subtraction|`SUB_Series`|✔️SUB|||✔️sub| -|MUL - Multiplication|`MUL_Series`|✔️MUL|||✔️mul| -|DIV - Division|`DIV_Series`|✔️DIV|||✔️div| -||||| -|**STATISTICS & NUMERICAL ANALYSIS**| -|||||| -|BIAS - Bias|`BIAS_Series`|||✔️bias| -|CORR - Pearson's Correlation Coefficient|`CORR_Series`|✔️CORREL|✔️GetCorrelation|| -|COVAR - Covariance|`COVAR_Series`||✔️GetCorrelation|| -|DECAY - Linear Decay|`DECAY_Series`|||decay|✔️decay| -|EDECAY - Exponential Decay|`DECAY_Series`|||decay|✔️edecay| -|ENTROPY - Entropy|`ENTROPY_Series`|||entropy|| -|KURTOSIS - Kurtosis|`KURT_Series`|||✔️kurtosis| -|SLOPE - Slope of Linear Regression|`SLOPE_Series`||✔️GetSlope||✔️linregslope| -|MAD - Mean Absolute Deviation|`MAD_Series`||✔️GetSmaAnalysis|✔️mad| -|MAE - Mean Absolute Error|`MAE_Series`|||| -|MAPE - Mean Absolute Percent Error|`MAPE_Series`||✔️GetSmaAnalysis|| -|MEDIAN - Median value|`MEDIAN_Series`|||✔️median| -|MSE - Mean Squared Error|`MSE_Series`||✔️GetSmaAnalysis|| -|SKEW - Skewness||||skew| -|⭐SDEV - Standard Deviation (Volatility)|`SDEV_Series`|✔️STDDEV|✔️GetStdDev|✔️stdev|✔️stddev| -|SSDEV - Sample Standard Deviation|`SSDEV_Series`|||✔️stdev| -|SMAPE - Symmetric Mean Absolute Percent Error|`SMAPE_Series`|||| -|VAR - Population Variance|`VAR_Series`|✔️VAR||✔️variance|✔️var| -|SVAR - Sample Variance|`SVAR_Series`|||✔️variance| -|QUANTILE - Quantile||||quantile| -|WMAPE - Weighted Mean Absolute Percent Error|`WMAPE_Series`|||| -|ZSCORE - Number of standard deviations from mean|`ZSCORE_Series`||✔️GetStdDev|✔️zscore| -|||||| -|**TREND INDICATORS & AVERAGES**| -|||||| -|AFIRMA - Autoregressive Finite Impulse Response Moving Average||||| -|ALMA - Arnaud Legoux Moving Average|`ALMA_Series`||✔️GetAlma|alma| -|DEMA - Double EMA Average|`DEMA_Series`|✔️DEMA|✔️GetDema|✔️dema|✔️dema| -|DWMA - Double WMA Average|`DWMA_Series`||||| -|⭐[EMA - Exponential Moving Average](EMA.md)|`EMA_Series`|✔️EMA|✔️GetEma|✔️ema|✔️ema| -|EPMA - Endpoint Moving Average|||GetEpma|| -|FRAMA - Fractal Adaptive Moving Average||||| -|FMA - Fibonacci's Weighted Moving Average|`FMA_Series`|||fwma| -|HILO - Gann High-Low Activator||||hilo| -|HEMA - Hull/EMA Average|`HEMA_Series`|||| -|Hilbert Transform Instantaneous Trendline||HT_TRENDLINE|GetHtTrendline|| -|⭐HMA - Hull Moving Average|`HMA_Series`||✔️GetHma|✔️hma|✔️hma| -|HWMA - Holt-Winter Moving Average|`HWMA_Series`|||✔️hwma| -|JMA - Jurik Moving Average|`JMA_Series`|||jma|| -|KAMA - Kaufman's Adaptive Moving Average|`KAMA_Series`|✔️KAMA|✔️GetKama|✔️kama|✔️kama| -|KDJ - KDJ Indicator (trend reversal)||||kdj| -|LSMA - Least Squares Moving Average|||GetEpma|| -|⭐MACD - Moving Average Convergence/Divergence|`MACD_Series`|✔️MACD|✔️GetMacd|✔️macd|✔️macd| -|MAMA - MESA Adaptive Moving Average|`MAMA_Series`|✔️MAMA|✔️GetMama|| -|MCGD - McGinley Dynamic||||mcgd| -|MMA - Modified Moving Average||||| -|PPMA - Pivot Point Moving Average||||| -|PWMA - Pascal's Weighted Moving Average||||pwma| -|⭐RMA - WildeR's Moving Average|`RMA_Series`|||✔️rma|✔️rma| -|SINWMA - Sine Weighted Moving Average||||sinwma| -|⭐[SMA - Simple Moving Average](SMA.md)|`SMA_Series`|✔️SMA|✔️GetSma|✔️sma|✔️sma| -|SMMA - Smoothed Moving Average|`SMMA_Series`||✔️GetSmma|| -|SSF - Ehler's Super Smoother Filter||||ssf| -|SUPERTREND - Supertrend||||supertrend| -|SWMA - Symmetric Weighted Moving Average||||swma| -|T3 - Tillson T3 Moving Average|`T3_Series`|✔️T3|✔️GetT3|✔️t3|| -|⭐TEMA - Triple EMA Average|`TEMA_Series`|✔️TEMA|✔️GetTema|✔️tema|✔️tema| -|⭐TRIMA - Triangular Moving Average|`TRIMA_Series`|✔️TRIMA||✔️trima|✔️trima| -|TSF - Time Series Forecast||TSF||| -|VIDYA - Variable Index Dynamic Average||||vidya|vidya| -|VORTEX - Vortex Indicator||||vortex| -|⭐WMA - Weighted Moving Average|`WMA_Series`|✔️WMA|✔️GetWma|✔️wma|✔️wma| -|ZLEMA - Zero Lag EMA Average|`ZLEMA_Series`|||✔️zlma|❌zlema| -|||||| -|**VOLATILITY INDICATORS**| -|||||| -|⭐ADL - Chaikin Accumulation Distribution Line|`ADL_Series`|✔️AD|✔️GetAdl|✔️ad|✔️ad| -|⭐ADOSC - Chaikin Accumulation Distribution Oscillator|`ADOSC_Series`|✔️ADOSC||✔️adosc|✔️adosc| -|⭐ATR - Average True Range|`ATR_Series`|✔️ATR|✔️GetAtr|✔️atr|✔️atr| -|ATRP - Average True Range Percent|`ATRP_Series`||✔️GetAtr|| -|BETA - Beta coefficient||BETA|GetBeta|| -|⭐BBANDS - Bollinger Bands®|`BBANDS_Series`|✔️BBANDS|✔️GetBollingerBands|✔️bbands|✔️bbands| -|CHAND - Chandelier Exit|||GetChandelier|| -|CRSI - Connor RSI|||GetConnorsRsi|| -|CVI - Chaikins Volatility|||||cvi| -|DON - Donchian Channels|||GetDonchian|| -|FCB - Fractal Chaos Bands|||GetFcb|| -|FISHER - Fisher Transform|||GetFcb||fisher| -|HV - Historical Volatility||||| -|ICH - Ichimoku|||GetIchimoku|| -|KEL - Keltner Channels|||GetKeltner|| -|NATR - Normalized Average True Range||NATR|GetAtr|| -|CHN - Price Channel Indicator||||| -|RSI - Relative Strength Index|`RSI_Series`|✔️RSI|✔️GetRsi|✔️rsi|✔️rsi| -|SAR - Parabolic Stop and Reverse||SAR|GetParabolicSar|| -|SRSI - Stochastic RSI||STOCHRSI|GetStochRsi|| -|STARC - Starc Bands||||| -|TR - True Range|`TR_Series`|✔️TRANGE|✔️GetTr|✔️true_range|✔️tr| -|UI - Ulcer Index||||| -|VSTOP - Volatility Stop||||| -|||||| -|**MOMENTUM INDICATORS & OSCILLATORS**| -|||||| -|AC - Acceleration Oscillator||||| -|ADX - Average Directional Movement Index||ADX|GetAdx||adx| -|ADXR - Average Directional Movement Index Rating||ADXR|GetAdx||adxr| -|AO - Awesome Oscillator|||GetAwesome||ao| -|APO - Absolute Price Oscillator||APO|||apo| -|AROON - Aroon oscillator||AROON|GetAroon||aroon| -|BOP - Balance of Power||BOP|GetBop||bop| -|CCI - Commodity Channel Index|`CCI_Series`|✔️CCI|✔️GetCci||❌cci| -|CFO - Chande Forcast Oscillator||||| -|CMO - Chande Momentum Oscillator|`CMO_Series`|❌CMO|✔️GetCmo|❌cmo|✔️cmo| -|COG - Center of Gravity||||| -|COPPOCK - Coppock Curve||||| -|CTI - Ehler's Correlation Trend Indicator||||| -|DPO - Detrended Price Oscillator|||GetDpo|| -|DMI - Directional Movement Index||DX|GetAdx|| -|EFI - Elder Ray's Force Index|||GetElderRay|| -|FOSC - Forecast oscillator|||||fosc| -|GAT - Alligator oscillator|||GetGator|| -|HURST - Hurst Exponent|||GetHurst|| -|KRI - Kairi Relative Index||||| -|KVO - Klinger Volume Oscillator|||||| -|MFI - Money Flow Index||MFI|GetMfi|| -|MOM - Momentum||MOM||| -|NVI - Negative Volume Index||||| -|PO - Price Oscillator||||| -|PPO - Percentage Price Oscillator||PPO||| -|PMO - Price Momentum Oscillator||||| -|PVI - Positive Volume Index||||| -|ROC - Rate of Change||MOM|GetRoc|| -|RVGI - Relative Vigor Index||||| -|SMI - Stochastic Momentum Index||||| -|STC - Schaff Trend Cycle||||| -|STOCH - Stochastic Oscillator||STOCH|GetStoch|| -|TRIX - 1-day ROC of TEMA|`TRIX_Series`|✔️TRIX|✔️GetTrix|✔️trix|❌trix| -|TSI - True Strength Index||||| -|UO - Ultimate Oscillator||ULTOSC|GetUltimate||ultosc| -|WILLR - Larry Williams' %R||WILLR|GetWilliamsR||willr| -|WGAT - Williams Alligator||||| -|||||| -|**VOLUME INDICATORS**| -|||||| -|AOBV - Archer On-Balance Volume||||| -|CMF - Chaikin Money Flow||||| -|EOM - Ease of Movement|||||emv| -|KVO - Klinger Volume Oscilaltor|||||kvo| -|OBV - On-Balance Volume|`OBV_Series`|✔️OBV|✔️GetObv|✔️obv|❌obv| -|PRS - Price Relative Strength|||| -|PVOL - Price-Volume||||| -|PVO - Percentage Volume Oscillator||||| -|PVR - Price Volume Rank||||| -|PVT - Price Volume Trend||||| -|VP - Volume Profile||||| -|VWAP - Volume Weighted Average Price||||| -|VWMA - Volume Weighted Moving Average|||||vwma| +# Coverage + +⭐= Calculation is validated against several TA libraries + +✔️= Validation tests passed + +❌= Issue + +|**BASIC TRANSFORMS**|**QuanTAlib**|**TA-LIB**|**Skender**|**Pandas TA**|**Tulip**| +|--|:--:|:--:|:--:|:--:|:--:| +|OC2 - (Open+Close)/2|️ `.OC2`||✔️CandlePart.OC2|| +|⭐HL2 - Median Price|`.HL2`|✔️MEDPRICE|✔️CandlePart.HL2|✔️hl2|✔️medprice| +|⭐HLC3 - Typical Price|`.HLC3`|✔️TYPPRICE|✔️CandlePart.HLC3|✔️hlc3|✔️typprice| +|OHL3 - (Open+High+Low)/3|`.OHL3`||✔️CandlePart.OHL3|| +|⭐OHLC4 - Average Price|`.OHLC4`|✔️AVGPRICE|️✔️CandlePart.OHLC4|✔️ohlc4|✔️avgprice| +|HLCC4 - Weighted Price|`.HLCC4`|✔️WCLPRICE|||✔️wcprice| +|MIDPOINT - Midpoint value|`MIDPOINT_Series`|✔️MIDPOINT||midpoint| +|MIDPRICE - Midpoint price|`MIDPRICE_Series`|✔️MIDPRICE||midprice| +|MAX - Max value|`MAX_Series`|✔️MAX|||✔️max| +|MIN - Min value|`MIN_Series`|✔️MIN|||✔️min| +|SUM - Summation|`SUM_Series`|✔️SUM|||✔️sum| +|ADD - Addition|`ADD_Series`|✔️ADD|||✔️add| +|SUB - Subtraction|`SUB_Series`|✔️SUB|||✔️sub| +|MUL - Multiplication|`MUL_Series`|✔️MUL|||✔️mul| +|DIV - Division|`DIV_Series`|✔️DIV|||✔️div| +||||| +|**STATISTICS & NUMERICAL ANALYSIS**| +|||||| +|BIAS - Bias|`BIAS_Series`|||✔️bias| +|CORR - Pearson's Correlation Coefficient|`CORR_Series`|✔️CORREL|✔️GetCorrelation|| +|COVAR - Covariance|`COVAR_Series`||✔️GetCorrelation|| +|DECAY - Linear Decay|`DECAY_Series`|||decay|✔️decay| +|EDECAY - Exponential Decay|`DECAY_Series`|||decay|✔️edecay| +|ENTROPY - Entropy|`ENTROPY_Series`|||entropy|| +|KURTOSIS - Kurtosis|`KURT_Series`|||✔️kurtosis| +|SLOPE - Slope of Linear Regression|`SLOPE_Series`||✔️GetSlope||✔️linregslope| +|MAD - Mean Absolute Deviation|`MAD_Series`||✔️GetSmaAnalysis|✔️mad| +|MAE - Mean Absolute Error|`MAE_Series`|||| +|MAPE - Mean Absolute Percent Error|`MAPE_Series`||✔️GetSmaAnalysis|| +|MEDIAN - Median value|`MEDIAN_Series`|||✔️median| +|MSE - Mean Squared Error|`MSE_Series`||✔️GetSmaAnalysis|| +|SKEW - Skewness||||skew| +|⭐SDEV - Standard Deviation (Volatility)|`SDEV_Series`|✔️STDDEV|✔️GetStdDev|✔️stdev|✔️stddev| +|SSDEV - Sample Standard Deviation|`SSDEV_Series`|||✔️stdev| +|SMAPE - Symmetric Mean Absolute Percent Error|`SMAPE_Series`|||| +|VAR - Population Variance|`VAR_Series`|✔️VAR||✔️variance|✔️var| +|SVAR - Sample Variance|`SVAR_Series`|||✔️variance| +|QUANTILE - Quantile||||quantile| +|WMAPE - Weighted Mean Absolute Percent Error|`WMAPE_Series`|||| +|ZSCORE - Number of standard deviations from mean|`ZSCORE_Series`||✔️GetStdDev|✔️zscore| +|||||| +|**TREND INDICATORS & AVERAGES**| +|||||| +|AFIRMA - Autoregressive Finite Impulse Response Moving Average||||| +|ALMA - Arnaud Legoux Moving Average|`ALMA_Series`||✔️GetAlma|alma| +|DEMA - Double EMA Average|`DEMA_Series`|✔️DEMA|✔️GetDema|✔️dema|✔️dema| +|DWMA - Double WMA Average|`DWMA_Series`||||| +|⭐[EMA - Exponential Moving Average](EMA.md)|`EMA_Series`|✔️EMA|✔️GetEma|✔️ema|✔️ema| +|EPMA - Endpoint Moving Average|||GetEpma|| +|FRAMA - Fractal Adaptive Moving Average||||| +|FMA - Fibonacci's Weighted Moving Average|`FMA_Series`|||fwma| +|HILO - Gann High-Low Activator||||hilo| +|HEMA - Hull/EMA Average|`HEMA_Series`|||| +|Hilbert Transform Instantaneous Trendline||HT_TRENDLINE|GetHtTrendline|| +|⭐HMA - Hull Moving Average|`HMA_Series`||✔️GetHma|✔️hma|✔️hma| +|HWMA - Holt-Winter Moving Average|`HWMA_Series`|||✔️hwma| +|JMA - Jurik Moving Average|`JMA_Series`|||jma|| +|KAMA - Kaufman's Adaptive Moving Average|`KAMA_Series`|✔️KAMA|✔️GetKama|✔️kama|✔️kama| +|KDJ - KDJ Indicator (trend reversal)||||kdj| +|LSMA - Least Squares Moving Average|||GetEpma|| +|⭐MACD - Moving Average Convergence/Divergence|`MACD_Series`|✔️MACD|✔️GetMacd|✔️macd|✔️macd| +|MAMA - MESA Adaptive Moving Average|`MAMA_Series`|✔️MAMA|✔️GetMama|| +|MCGD - McGinley Dynamic||||mcgd| +|MMA - Modified Moving Average||||| +|PPMA - Pivot Point Moving Average||||| +|PWMA - Pascal's Weighted Moving Average||||pwma| +|⭐RMA - WildeR's Moving Average|`RMA_Series`|||✔️rma|✔️rma| +|SINWMA - Sine Weighted Moving Average||||sinwma| +|⭐[SMA - Simple Moving Average](SMA.md)|`SMA_Series`|✔️SMA|✔️GetSma|✔️sma|✔️sma| +|SMMA - Smoothed Moving Average|`SMMA_Series`||✔️GetSmma|| +|SSF - Ehler's Super Smoother Filter||||ssf| +|SUPERTREND - Supertrend||||supertrend| +|SWMA - Symmetric Weighted Moving Average||||swma| +|T3 - Tillson T3 Moving Average|`T3_Series`|✔️T3|✔️GetT3|✔️t3|| +|⭐TEMA - Triple EMA Average|`TEMA_Series`|✔️TEMA|✔️GetTema|✔️tema|✔️tema| +|⭐TRIMA - Triangular Moving Average|`TRIMA_Series`|✔️TRIMA||✔️trima|✔️trima| +|TSF - Time Series Forecast||TSF||| +|VIDYA - Variable Index Dynamic Average||||vidya|vidya| +|VORTEX - Vortex Indicator||||vortex| +|⭐WMA - Weighted Moving Average|`WMA_Series`|✔️WMA|✔️GetWma|✔️wma|✔️wma| +|ZLEMA - Zero Lag EMA Average|`ZLEMA_Series`|||✔️zlma|❌zlema| +|||||| +|**VOLATILITY INDICATORS**| +|||||| +|⭐ADL - Chaikin Accumulation Distribution Line|`ADL_Series`|✔️AD|✔️GetAdl|✔️ad|✔️ad| +|⭐ADOSC - Chaikin Accumulation Distribution Oscillator|`ADOSC_Series`|✔️ADOSC||✔️adosc|✔️adosc| +|⭐ATR - Average True Range|`ATR_Series`|✔️ATR|✔️GetAtr|✔️atr|✔️atr| +|ATRP - Average True Range Percent|`ATRP_Series`||✔️GetAtr|| +|BETA - Beta coefficient||BETA|GetBeta|| +|⭐BBANDS - Bollinger Bands®|`BBANDS_Series`|✔️BBANDS|✔️GetBollingerBands|✔️bbands|✔️bbands| +|CHAND - Chandelier Exit|||GetChandelier|| +|CRSI - Connor RSI|||GetConnorsRsi|| +|CVI - Chaikins Volatility|||||cvi| +|DON - Donchian Channels|||GetDonchian|| +|FCB - Fractal Chaos Bands|||GetFcb|| +|FISHER - Fisher Transform|||GetFcb||fisher| +|HV - Historical Volatility||||| +|ICH - Ichimoku|||GetIchimoku|| +|KEL - Keltner Channels|||GetKeltner|| +|NATR - Normalized Average True Range||NATR|GetAtr|| +|CHN - Price Channel Indicator||||| +|RSI - Relative Strength Index|`RSI_Series`|✔️RSI|✔️GetRsi|✔️rsi|✔️rsi| +|SAR - Parabolic Stop and Reverse||SAR|GetParabolicSar|| +|SRSI - Stochastic RSI||STOCHRSI|GetStochRsi|| +|STARC - Starc Bands||||| +|TR - True Range|`TR_Series`|✔️TRANGE|✔️GetTr|✔️true_range|✔️tr| +|UI - Ulcer Index||||| +|VSTOP - Volatility Stop||||| +|||||| +|**MOMENTUM INDICATORS & OSCILLATORS**| +|||||| +|AC - Acceleration Oscillator||||| +|ADX - Average Directional Movement Index||ADX|GetAdx||adx| +|ADXR - Average Directional Movement Index Rating||ADXR|GetAdx||adxr| +|AO - Awesome Oscillator|||GetAwesome||ao| +|APO - Absolute Price Oscillator||APO|||apo| +|AROON - Aroon oscillator||AROON|GetAroon||aroon| +|BOP - Balance of Power||BOP|GetBop||bop| +|CCI - Commodity Channel Index|`CCI_Series`|✔️CCI|✔️GetCci||❌cci| +|CFO - Chande Forcast Oscillator||||| +|CMO - Chande Momentum Oscillator|`CMO_Series`|❌CMO|✔️GetCmo|❌cmo|✔️cmo| +|COG - Center of Gravity||||| +|COPPOCK - Coppock Curve||||| +|CTI - Ehler's Correlation Trend Indicator||||| +|DPO - Detrended Price Oscillator|||GetDpo|| +|DMI - Directional Movement Index||DX|GetAdx|| +|EFI - Elder Ray's Force Index|||GetElderRay|| +|FOSC - Forecast oscillator|||||fosc| +|GAT - Alligator oscillator|||GetGator|| +|HURST - Hurst Exponent|||GetHurst|| +|KRI - Kairi Relative Index||||| +|KVO - Klinger Volume Oscillator|||||| +|MFI - Money Flow Index||MFI|GetMfi|| +|MOM - Momentum||MOM||| +|NVI - Negative Volume Index||||| +|PO - Price Oscillator||||| +|PPO - Percentage Price Oscillator||PPO||| +|PMO - Price Momentum Oscillator||||| +|PVI - Positive Volume Index||||| +|ROC - Rate of Change||MOM|GetRoc|| +|RVGI - Relative Vigor Index||||| +|SMI - Stochastic Momentum Index||||| +|STC - Schaff Trend Cycle||||| +|STOCH - Stochastic Oscillator||STOCH|GetStoch|| +|TRIX - 1-day ROC of TEMA|`TRIX_Series`|✔️TRIX|✔️GetTrix|✔️trix|❌trix| +|TSI - True Strength Index||||| +|UO - Ultimate Oscillator||ULTOSC|GetUltimate||ultosc| +|WILLR - Larry Williams' %R||WILLR|GetWilliamsR||willr| +|WGAT - Williams Alligator||||| +|||||| +|**VOLUME INDICATORS**| +|||||| +|AOBV - Archer On-Balance Volume||||| +|CMF - Chaikin Money Flow||||| +|EOM - Ease of Movement|||||emv| +|KVO - Klinger Volume Oscilaltor|||||kvo| +|OBV - On-Balance Volume|`OBV_Series`|✔️OBV|✔️GetObv|✔️obv|❌obv| +|PRS - Price Relative Strength|||| +|PVOL - Price-Volume||||| +|PVO - Percentage Volume Oscillator||||| +|PVR - Price Volume Rank||||| +|PVT - Price Volume Trend||||| +|VP - Volume Profile||||| +|VWAP - Volume Weighted Average Price||||| +|VWMA - Volume Weighted Moving Average|||||vwma| diff --git a/archive/docs/readme.md b/archive/docs/readme.md new file mode 100644 index 00000000..a8c3d92d --- /dev/null +++ b/archive/docs/readme.md @@ -0,0 +1,44 @@ +# QuanTAlib - quantitative technical indicators for Quantower and other C#-based trading platorms + +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=mihakralj_QuanTAlib&metric=ncloc)](https://sonarcloud.io/summary/overall?id=mihakralj_QuanTAlib) +[![Codacy grade](https://img.shields.io/codacy/grade/b1f9109222234c87bce45f1fd4c63aee?style=flat-square)](https://app.codacy.com/gh/mihakralj/QuanTAlib/dashboard) +[![codecov](https://codecov.io/gh/mihakralj/QuanTAlib/branch/main/graph/badge.svg?style=flat-square&token=YNMJRGKMTJ?style=flat-square)](https://codecov.io/gh/mihakralj/QuanTAlib) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=mihakralj_QuanTAlib&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=mihakralj_QuanTAlib) +[![CodeFactor](https://www.codefactor.io/repository/github/mihakralj/quantalib/badge/main)](https://www.codefactor.io/repository/github/mihakralj/quantalib/overview/main) + +[![Nuget](https://img.shields.io/nuget/v/QuanTAlib?style=flat-square)](https://www.nuget.org/packages/QuanTAlib/) +![GitHub last commit](https://img.shields.io/github/last-commit/mihakralj/QuanTAlib) +[![Nuget](https://img.shields.io/nuget/dt/QuanTAlib?style=flat-square)](https://www.nuget.org/packages/QuanTAlib/) +[![GitHub watchers](https://img.shields.io/github/watchers/mihakralj/QuanTAlib?style=flat-square)](https://github.com/mihakralj/QuanTAlib/watchers) +[![.NET7.0](https://img.shields.io/badge/.NET-7.0%20%7C%206.0%20%7C%204.8-blue?style=flat-square)](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) + +**Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for analyzing quotes with [Quantower](https://www.quantower.com/) and other C#-based trading platforms. + +**QuanTAlib** is written with some specific design criteria in mind - why there is '_yet another C# TA library_': + +- Prioritize **real-time data analysis** (series can add new data and indicator doesn't have to re-calculate the whole history) +- **Allow updates** to the last quote and adjusting the calculation to the still-forming bar +- **Calculate early data right** - output data is as valid as mathematically possible from the first value onwards + +![Alt text](./img/quotes.gif) + +If not obvious, QuanTAlib is intended for developers, and it does not focus on sources of OHLCV quotes. There are some very basic data feeds available to use in the learning process: `RND_Feed` and `GBM_Feed` for random data, `Yahoo_Feed` and `Alphavantage_Feed` for a quick grab of daily data of US stock market. + +See [Getting Started](https://github.com/mihakralj/QuanTAlib/blob/main/Docs/getting_started.ipynb) .NET interactive notebook to get a feel how library works. Developers can use QuanTAlib in [Polyglot Notebooks](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) or in console apps, but the best usage of the library is with C#-enabled trading platforms - see **QuanTower_Charts** folder for Quantower examples and check **Releases** for compiled Quantower DLL. + +### Coverage + +[List of all indicators - current and planned](indicators.md) + +### Validation + +QuanTAlib uses validation tests with four other TA libraries to assure accuracy and validity of results: + +- [TA-LIB](https://www.ta-lib.org/function.html) +- [Skender Stock Indicators](https://dotnet.stockindicators.dev/) +- [Pandas-TA](https://twopirllc.github.io/pandas-ta/) +- [Tulip Indicators](https://tulipindicators.org/) + +### Questions + +[Some most common questions addressed](QA.md) \ No newline at end of file diff --git a/docs/To illuminate and standardize the vast d.md b/docs/To illuminate and standardize the vast d.md deleted file mode 100644 index 941489a5..00000000 --- a/docs/To illuminate and standardize the vast d.md +++ /dev/null @@ -1,2 +0,0 @@ -"To use unique insights from EPAM's history, expertise, and innovative spirit we want to recalibrate technology strategies to deliver solutions that are not just innovative, but driven by value creation. We envision a future where every client engagement is delivers integrated value from strategy to optimization, and where our technical thought leadership is a benchmark for the industry, ensuring that EPAM is synonymous with transformative digital engineering." - diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 6266971c..24d3cd5f 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,24 +1,56 @@ -* [Home](/) - * [FAQ - Frequently asked questions answered](QA.md) - -* [List of all Indicators](indicators.md "Indicators coverage") - - * [SMA - Simple Moving Average](SMA.md) - * [EMA - Exponential Moving Average](EMA.md) - * [WMA - Weighted Moving Average](WMA.md) - * [T3 - Tillson T3 Exponential MA](T3.md) - * [SMMA - Smoothed Moving Average](SMMA.md) - * [TRIMA - Triangular Moving Average](TRIMA.md) - * [DWMA - Double Weighted Moving Average](DWMA.md) - * [FMA - Fibonacci Moving Average](FMA.md) - * [DEMA - Double Exponential MA](DEMA.md) - * [TEMA - Triple Exponential MA](TEMA.md) - * [ALMA - Arnaud Legoux Moving Average](ALMA.md) - * [HMA - Hull Moving Average](HMA.md) - * [HEMA - Hull/Exponential Moving Average](HEMA.md) - * [HWMA - Holt-Winter Moving Average](HWMA.md) - * [MAMA - MESA Adaptive Moving Average](MAMA.md) - * [KAMA - Kaufman Adaptive Moving Average](KAMA.md) - * [ZLEMA - Zero-Lag Exponential MA](ZLEMA.md) - * [JMA - Jurik Moving Average](JMA.md) - +* [QuanTAlib](/) +* Library Structure + * Basic types + * Tvalue & TSeries + * TBar & TBarSeries + * Usage +* Using in Quantower +* [Indicators](indicators/indicators.md) + * Averages & Trends + * ALMA - Arnaud Legoux Moving Average + * [Charts](indicators/averages/alma/charts.md) + * DEMA - Double Exponential Moving Average + * [Charts](indicators/averages/dema/charts.md) + * DSMA - Deviation Scaled Moving Average + * [Charts](indicators/averages/dsma/charts.md) + * DWMA - Double Weighted Moving Average + * [Charts](indicators/averages/dwma/charts.md) + * [EMA - Exponential Moving Average](indicators/averages/ema/ema.md) + * [Calculation](indicators/averages/ema/calculation.md) + * [Quality](indicators/averages/ema/quality.md) + * [Charts](indicators/averages/ema/charts.md) + * EPMA - Endpoint Moving Average + * FRAMA - Fractal Adaptive Moving Average + * FWMA - Fibonacci-Weighted Moving Average + * GMA - Gaussian-Weighted Moving Average + * HMA - Hull Moving Average + * HTIT - Hilbert Transform Instantaneous Trendline + * HWMA - Holt-Winter Moving Average + * JMA - Jurik Moving Average + * KAMA - Kaufman's Adaptive Moving Average + * LTMA - Laguerre Transform Moving Average + * MAAF - Median-Average Adaptive Filter + * MAMA - MESA Adaptive Moving Average + * MGDI - McGinley Dynamic Index + * MMA - Modified Moving Average + * QEMA - Quad Exponential Moving Average + * REMA - Regularized Exponential Moving Average + * RMA - wildeR Moving Average + * SINEMA - Sine-Weighted Moving Average + * [SMA - Simple Moving Average](indicators/averages/sma/sma.md) + * [Charts](indicators/averages/sma/charts.md) + * SMMA - Smoothed Moving Average + * T3 - Tillson T3 Moving Average + * TEMA - Triple Exponential Moving Average + * TRIMA - Triangular Moving Average + * VIDYA - Variable Index Dynamic Average + * WMA - Weighted Moving Average + * ZLEMA - Weighted Moving Average + * Basic Data Transforms + * [Statistics & Numerical Analysis](indicators/statistics/list.md) + * Volatility + * Volume + * Momentum & Oscillators +* Development + * [VS Code](setup/vscode.md) + * [DotPeek](setup/dotpeek.md) diff --git a/docs/img/dotpeek.png b/docs/img/dotpeek.png new file mode 100644 index 00000000..5d2bb67f Binary files /dev/null and b/docs/img/dotpeek.png differ diff --git a/docs/img/emaweights.svg b/docs/img/emaweights.svg new file mode 100644 index 00000000..384ce713 --- /dev/null +++ b/docs/img/emaweights.svg @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EMA(10) Weights Chart + + + + + + + + + + + + -1 + + + + 0 + + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + 5 + + + + 6 + + + + 7 + + + + 8 + + + + 9 + + + + 10 + + + + 11 + + + + 12 + + + + 13 + + + + 14 + + + + 15 + + + + 16 + + + + 17 + + + + 18 + + + + 19 + + + + 20 + + + + 21 + + + + 22 + + + + 23 + + + + 24 + + + + 25 + + + + 26 + + + + 27 + + + + 28 + + + + 29 + + + + 30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.02 + + + + 0.04 + + + + 0.06 + + + + 0.08 + + + + 0.1 + + + + 0.12 + + + + 0.14 + + + + 0.16 + + + + 0.18 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/index.html b/docs/index.html index 4dbc1941..46d54d15 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,11 +3,36 @@ Document - + - - + + +
@@ -18,17 +43,26 @@ subMaxLevel: 1, name: '', repo: '', + sidebarDisplayLevel: 2, + themeable: { + readyTransition: true, + responsiveTables: true + }, latex: { - inlineMath : [['$', '$'], ['\\(', '\\)']], // default - displayMath : [['$$', '$$']], // default - } + inlineMath: [['$', '$'], ['\\(', '\\)']], + displayMath: [['$$', '$$']], + } }; + + - - - - - + + + + + + + - + \ No newline at end of file diff --git a/docs/indicators/averages/alma/charts.dib b/docs/indicators/averages/alma/charts.dib new file mode 100644 index 00000000..c40b915a --- /dev/null +++ b/docs/indicators/averages/alma/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "ALMA"; +int p = 10; +Func Indicator = period => new Alma(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/alma/charts.md b/docs/indicators/averages/alma/charts.md new file mode 100644 index 00000000..64e9389b --- /dev/null +++ b/docs/indicators/averages/alma/charts.md @@ -0,0 +1,3 @@ +# ALMA Charts + +![](img/ALMA10_Spike.svg) ![](img/ALMA10_Impulse.svg) ![](img/ALMA10_Triangle.svg) ![](img/ALMA10_Sawtooth.svg) ![](img/ALMA10_Sine.svg) ![](img/ALMA10_Chirp.svg) ![](img/ALMA10_White.svg) ![](img/ALMA10_Gauss.svg) ![](img/ALMA10_B.svg) ![](img/ALMA10_HF.svg) ![](img/ALMA10_ImpulseHF.svg) ![](img/ALMA10_SawtoothHF.svg) ![](img/ALMA10_SineG.svg) ![](img/ALMA10_ChirpG.svg) ![](img/ALMA10_Complex.svg) ![](img/ALMA10_Market.svg) diff --git a/docs/indicators/averages/alma/img/ALMA10_B.svg b/docs/indicators/averages/alma/img/ALMA10_B.svg new file mode 100644 index 00000000..a00036ba --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_B.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + B - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Chirp.svg b/docs/indicators/averages/alma/img/ALMA10_Chirp.svg new file mode 100644 index 00000000..3f24614b --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Chirp.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Chirp - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_ChirpG.svg b/docs/indicators/averages/alma/img/ALMA10_ChirpG.svg new file mode 100644 index 00000000..5b4d02cc --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_ChirpG.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Complex.svg b/docs/indicators/averages/alma/img/ALMA10_Complex.svg new file mode 100644 index 00000000..9ff8437b --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Complex.svg @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Gauss.svg b/docs/indicators/averages/alma/img/ALMA10_Gauss.svg new file mode 100644 index 00000000..4ea44cb0 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_HF.svg b/docs/indicators/averages/alma/img/ALMA10_HF.svg new file mode 100644 index 00000000..d6c56a19 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_HF.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + HF - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Impulse.svg b/docs/indicators/averages/alma/img/ALMA10_Impulse.svg new file mode 100644 index 00000000..371c60c0 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Impulse.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_ImpulseHF.svg b/docs/indicators/averages/alma/img/ALMA10_ImpulseHF.svg new file mode 100644 index 00000000..e83c7910 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Market.svg b/docs/indicators/averages/alma/img/ALMA10_Market.svg new file mode 100644 index 00000000..bf76c722 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Sawtooth.svg b/docs/indicators/averages/alma/img/ALMA10_Sawtooth.svg new file mode 100644 index 00000000..82fd1081 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Sawtooth.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_SawtoothHF.svg b/docs/indicators/averages/alma/img/ALMA10_SawtoothHF.svg new file mode 100644 index 00000000..8aa4cfab --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_SawtoothHF.svg @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Sine.svg b/docs/indicators/averages/alma/img/ALMA10_Sine.svg new file mode 100644 index 00000000..cf18eba2 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Sine.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Sine - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_SineG.svg b/docs/indicators/averages/alma/img/ALMA10_SineG.svg new file mode 100644 index 00000000..a3611953 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_SineG.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Spike.svg b/docs/indicators/averages/alma/img/ALMA10_Spike.svg new file mode 100644 index 00000000..1398ef2a --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Spike.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_Triangle.svg b/docs/indicators/averages/alma/img/ALMA10_Triangle.svg new file mode 100644 index 00000000..53a38259 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - ALMA(10) + + diff --git a/docs/indicators/averages/alma/img/ALMA10_White.svg b/docs/indicators/averages/alma/img/ALMA10_White.svg new file mode 100644 index 00000000..cba2d816 --- /dev/null +++ b/docs/indicators/averages/alma/img/ALMA10_White.svg @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - ALMA(10) + + diff --git a/docs/indicators/averages/dema/charts.dib b/docs/indicators/averages/dema/charts.dib new file mode 100644 index 00000000..8b2f0f40 --- /dev/null +++ b/docs/indicators/averages/dema/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "DEMA"; +int p = 10; +Func Indicator = period => new Dema(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/dema/charts.md b/docs/indicators/averages/dema/charts.md new file mode 100644 index 00000000..af4ac622 --- /dev/null +++ b/docs/indicators/averages/dema/charts.md @@ -0,0 +1,3 @@ +# DEMA Charts + +![](img/DEMA10_Spike.svg) ![](img/DEMA10_Impulse.svg) ![](img/DEMA10_Triangle.svg) ![](img/DEMA10_Sawtooth.svg) ![](img/DEMA10_Sine.svg) ![](img/DEMA10_Chirp.svg) ![](img/DEMA10_White.svg) ![](img/DEMA10_Gauss.svg) ![](img/DEMA10_B.svg) ![](img/DEMA10_HF.svg) ![](img/DEMA10_ImpulseHF.svg) ![](img/DEMA10_SawtoothHF.svg) ![](img/DEMA10_SineG.svg) ![](img/DEMA10_ChirpG.svg) ![](img/DEMA10_Complex.svg) ![](img/DEMA10_Market.svg) diff --git a/docs/indicators/averages/dema/img/DEMA10_B.svg b/docs/indicators/averages/dema/img/DEMA10_B.svg new file mode 100644 index 00000000..3a5390ab --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_B.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + B - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Chirp.svg b/docs/indicators/averages/dema/img/DEMA10_Chirp.svg new file mode 100644 index 00000000..404a6274 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Chirp.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Chirp - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_ChirpG.svg b/docs/indicators/averages/dema/img/DEMA10_ChirpG.svg new file mode 100644 index 00000000..7f27ab8e --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_ChirpG.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Complex.svg b/docs/indicators/averages/dema/img/DEMA10_Complex.svg new file mode 100644 index 00000000..931f69c1 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Complex.svg @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Gauss.svg b/docs/indicators/averages/dema/img/DEMA10_Gauss.svg new file mode 100644 index 00000000..19af7216 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_HF.svg b/docs/indicators/averages/dema/img/DEMA10_HF.svg new file mode 100644 index 00000000..17be89df --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_HF.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + HF - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Impulse.svg b/docs/indicators/averages/dema/img/DEMA10_Impulse.svg new file mode 100644 index 00000000..927ace82 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Impulse.svg @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_ImpulseHF.svg b/docs/indicators/averages/dema/img/DEMA10_ImpulseHF.svg new file mode 100644 index 00000000..3905bb23 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Market.svg b/docs/indicators/averages/dema/img/DEMA10_Market.svg new file mode 100644 index 00000000..e6364a06 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Sawtooth.svg b/docs/indicators/averages/dema/img/DEMA10_Sawtooth.svg new file mode 100644 index 00000000..b21d2cff --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Sawtooth.svg @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -5 + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_SawtoothHF.svg b/docs/indicators/averages/dema/img/DEMA10_SawtoothHF.svg new file mode 100644 index 00000000..c69f61a3 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_SawtoothHF.svg @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Sine.svg b/docs/indicators/averages/dema/img/DEMA10_Sine.svg new file mode 100644 index 00000000..30fa7e82 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Sine.svg @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + Sine - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_SineG.svg b/docs/indicators/averages/dema/img/DEMA10_SineG.svg new file mode 100644 index 00000000..b0bab4fe --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_SineG.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Spike.svg b/docs/indicators/averages/dema/img/DEMA10_Spike.svg new file mode 100644 index 00000000..6d0c6fcb --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Spike.svg @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_Triangle.svg b/docs/indicators/averages/dema/img/DEMA10_Triangle.svg new file mode 100644 index 00000000..d5f7bf0a --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - DEMA(10) + + diff --git a/docs/indicators/averages/dema/img/DEMA10_White.svg b/docs/indicators/averages/dema/img/DEMA10_White.svg new file mode 100644 index 00000000..ba53cde0 --- /dev/null +++ b/docs/indicators/averages/dema/img/DEMA10_White.svg @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - DEMA(10) + + diff --git a/docs/indicators/averages/dsma/charts.dib b/docs/indicators/averages/dsma/charts.dib new file mode 100644 index 00000000..9cf4093b --- /dev/null +++ b/docs/indicators/averages/dsma/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "DSMA"; +int p = 10; +Func Indicator = period => new Dsma(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/dsma/charts.md b/docs/indicators/averages/dsma/charts.md new file mode 100644 index 00000000..1df86ea9 --- /dev/null +++ b/docs/indicators/averages/dsma/charts.md @@ -0,0 +1,3 @@ +# DSMA Charts + +![](img/DSMA10_Spike.svg) ![](img/DSMA10_Impulse.svg) ![](img/DSMA10_Triangle.svg) ![](img/DSMA10_Sawtooth.svg) ![](img/DSMA10_Sine.svg) ![](img/DSMA10_Chirp.svg) ![](img/DSMA10_White.svg) ![](img/DSMA10_Gauss.svg) ![](img/DSMA10_B.svg) ![](img/DSMA10_HF.svg) ![](img/DSMA10_ImpulseHF.svg) ![](img/DSMA10_SawtoothHF.svg) ![](img/DSMA10_SineG.svg) ![](img/DSMA10_ChirpG.svg) ![](img/DSMA10_Complex.svg) ![](img/DSMA10_Market.svg) diff --git a/docs/indicators/averages/dsma/img/DSMA10_B.svg b/docs/indicators/averages/dsma/img/DSMA10_B.svg new file mode 100644 index 00000000..eee2d9e1 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_B.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -2 + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + B - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Chirp.svg b/docs/indicators/averages/dsma/img/DSMA10_Chirp.svg new file mode 100644 index 00000000..c8e17edd --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Chirp.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Chirp - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_ChirpG.svg b/docs/indicators/averages/dsma/img/DSMA10_ChirpG.svg new file mode 100644 index 00000000..18a25285 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_ChirpG.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Complex.svg b/docs/indicators/averages/dsma/img/DSMA10_Complex.svg new file mode 100644 index 00000000..c008e8aa --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Complex.svg @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Gauss.svg b/docs/indicators/averages/dsma/img/DSMA10_Gauss.svg new file mode 100644 index 00000000..ea3d8540 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_HF.svg b/docs/indicators/averages/dsma/img/DSMA10_HF.svg new file mode 100644 index 00000000..9c80e8aa --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_HF.svg @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + HF - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Impulse.svg b/docs/indicators/averages/dsma/img/DSMA10_Impulse.svg new file mode 100644 index 00000000..61c61961 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Impulse.svg @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + 1.2 + + + + 1.4 + + + + 1.6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_ImpulseHF.svg b/docs/indicators/averages/dsma/img/DSMA10_ImpulseHF.svg new file mode 100644 index 00000000..c2828409 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Market.svg b/docs/indicators/averages/dsma/img/DSMA10_Market.svg new file mode 100644 index 00000000..fef0aecc --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Sawtooth.svg b/docs/indicators/averages/dsma/img/DSMA10_Sawtooth.svg new file mode 100644 index 00000000..1536998b --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Sawtooth.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -10 + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_SawtoothHF.svg b/docs/indicators/averages/dsma/img/DSMA10_SawtoothHF.svg new file mode 100644 index 00000000..14fbfe97 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_SawtoothHF.svg @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -10 + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Sine.svg b/docs/indicators/averages/dsma/img/DSMA10_Sine.svg new file mode 100644 index 00000000..26067db4 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Sine.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Sine - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_SineG.svg b/docs/indicators/averages/dsma/img/DSMA10_SineG.svg new file mode 100644 index 00000000..e9ff3415 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_SineG.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Spike.svg b/docs/indicators/averages/dsma/img/DSMA10_Spike.svg new file mode 100644 index 00000000..1961a6ae --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Spike.svg @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_Triangle.svg b/docs/indicators/averages/dsma/img/DSMA10_Triangle.svg new file mode 100644 index 00000000..8a4478a2 --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - DSMA(10) + + diff --git a/docs/indicators/averages/dsma/img/DSMA10_White.svg b/docs/indicators/averages/dsma/img/DSMA10_White.svg new file mode 100644 index 00000000..4d5eaa7e --- /dev/null +++ b/docs/indicators/averages/dsma/img/DSMA10_White.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + + + + -0.6 + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - DSMA(10) + + diff --git a/docs/indicators/averages/dwma/charts.dib b/docs/indicators/averages/dwma/charts.dib new file mode 100644 index 00000000..8542000b --- /dev/null +++ b/docs/indicators/averages/dwma/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "DWMA"; +int p = 10; +Func Indicator = period => new Dwma(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/dwma/charts.md b/docs/indicators/averages/dwma/charts.md new file mode 100644 index 00000000..b29c1235 --- /dev/null +++ b/docs/indicators/averages/dwma/charts.md @@ -0,0 +1,3 @@ +# DWMA Charts + +![](img/DWMA10_Spike.svg) ![](img/DWMA10_Impulse.svg) ![](img/DWMA10_Triangle.svg) ![](img/DWMA10_Sawtooth.svg) ![](img/DWMA10_Sine.svg) ![](img/DWMA10_Chirp.svg) ![](img/DWMA10_White.svg) ![](img/DWMA10_Gauss.svg) ![](img/DWMA10_B.svg) ![](img/DWMA10_HF.svg) ![](img/DWMA10_ImpulseHF.svg) ![](img/DWMA10_SawtoothHF.svg) ![](img/DWMA10_SineG.svg) ![](img/DWMA10_ChirpG.svg) ![](img/DWMA10_Complex.svg) ![](img/DWMA10_Market.svg) diff --git a/docs/indicators/averages/dwma/img/DWMA10_B.svg b/docs/indicators/averages/dwma/img/DWMA10_B.svg new file mode 100644 index 00000000..08e5651a --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_B.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + B - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Chirp.svg b/docs/indicators/averages/dwma/img/DWMA10_Chirp.svg new file mode 100644 index 00000000..251d571e --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Chirp.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Chirp - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_ChirpG.svg b/docs/indicators/averages/dwma/img/DWMA10_ChirpG.svg new file mode 100644 index 00000000..cd4f6cc4 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_ChirpG.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Complex.svg b/docs/indicators/averages/dwma/img/DWMA10_Complex.svg new file mode 100644 index 00000000..c9cfb0c8 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Complex.svg @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Gauss.svg b/docs/indicators/averages/dwma/img/DWMA10_Gauss.svg new file mode 100644 index 00000000..13a5e373 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_HF.svg b/docs/indicators/averages/dwma/img/DWMA10_HF.svg new file mode 100644 index 00000000..08028b45 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_HF.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + HF - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Impulse.svg b/docs/indicators/averages/dwma/img/DWMA10_Impulse.svg new file mode 100644 index 00000000..8a8957e7 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Impulse.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_ImpulseHF.svg b/docs/indicators/averages/dwma/img/DWMA10_ImpulseHF.svg new file mode 100644 index 00000000..b0286f37 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Market.svg b/docs/indicators/averages/dwma/img/DWMA10_Market.svg new file mode 100644 index 00000000..eb1b87b6 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Sawtooth.svg b/docs/indicators/averages/dwma/img/DWMA10_Sawtooth.svg new file mode 100644 index 00000000..0393a32a --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Sawtooth.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_SawtoothHF.svg b/docs/indicators/averages/dwma/img/DWMA10_SawtoothHF.svg new file mode 100644 index 00000000..12b7c88c --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_SawtoothHF.svg @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Sine.svg b/docs/indicators/averages/dwma/img/DWMA10_Sine.svg new file mode 100644 index 00000000..e81ae1b2 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Sine.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Sine - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_SineG.svg b/docs/indicators/averages/dwma/img/DWMA10_SineG.svg new file mode 100644 index 00000000..1265ef2e --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_SineG.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Spike.svg b/docs/indicators/averages/dwma/img/DWMA10_Spike.svg new file mode 100644 index 00000000..1a42610d --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Spike.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_Triangle.svg b/docs/indicators/averages/dwma/img/DWMA10_Triangle.svg new file mode 100644 index 00000000..aa055fa2 --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - DWMA(10) + + diff --git a/docs/indicators/averages/dwma/img/DWMA10_White.svg b/docs/indicators/averages/dwma/img/DWMA10_White.svg new file mode 100644 index 00000000..497dd03e --- /dev/null +++ b/docs/indicators/averages/dwma/img/DWMA10_White.svg @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - DWMA(10) + + diff --git a/docs/indicators/averages/ema/calculation.md b/docs/indicators/averages/ema/calculation.md new file mode 100644 index 00000000..5793805e --- /dev/null +++ b/docs/indicators/averages/ema/calculation.md @@ -0,0 +1,55 @@ +## EMA - Calculation Method + +The EMA calculation utilizes a weighting multiplier, typically denoted as the smoothing factor ($alpha$). This factor is calculated as: + +$alpha = \frac{2}{period + 1}$ + +where 'period' represents the chosen period for the EMA. + +The general formula for EMA required for arithmetic operations: + +$EMA_n = (data_{n} \times alpha) + (EMA_{n-1} \times (1 - alpha))$ + +or in optimized form (requires only three arithmetic operations instead of four): + +$EMA_n = {alpha}\times ({data_{n}} - EMA_{n-1}) + EMA_{n-1}$ + + + +When calculating the Exponential Moving Average (EMA) and there is not enough data (n < period), several approaches can be considered. Each method has its own pros and cons: + +#### 1. Assume all previous values were 0 + +$EMA_0 = 0$ \ +$EMA_n = alpha \times (data_n - EMA_{n-1}) + EMA_{n-1}$ + +- Will lead to significant underestimation of EMA in early periods + +#### 2. Calculate as if all previous values were the same as the first value + +$EMA_0 = data_0$ \ +$EMA_n = alpha \times (data_n - EMA_{n-1}) + EMA_{n-1}$ + +- Will overestimate early EMA if initial data point is far from representative + +#### 3. Use SMA instead of EMA for the first period + +$EMA_n = \left\{ \begin{array}{cl} +\frac{1}{p}\left( data_{n}-data_{n-p}\right)+SMA_{n-1} & : \ n \leq period \\ +{alpha}\times ({data_{n}} - EMA_{n-1}) + EMA_{n-1} & : \ n > period +\end{array} \right.$ + +- Creates a discontinuity when switching from SMA to EMA + + + +### Conclusion + +The choice of method depends on the specific requirements of the application: + +- Method 1 is suitable for applications where underestimation in early periods is acceptable. +- Method 2 is beneficial when a smooth transition is crucial and the initial data point is representative. +- Method 3 is appropriate when simplicity is preferred and a clear distinction between SMA and EMA is acceptable. +- Method 4 offers a good balance between adaptability and maintaining the EMA concept, but may require additional explanation to users. + + diff --git a/docs/indicators/averages/ema/charts.dib b/docs/indicators/averages/ema/charts.dib new file mode 100644 index 00000000..aef824dc --- /dev/null +++ b/docs/indicators/averages/ema/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "EMA"; +int p = 10; +Func Indicator = period => new Ema(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/ema/charts.md b/docs/indicators/averages/ema/charts.md new file mode 100644 index 00000000..ddcd25f0 --- /dev/null +++ b/docs/indicators/averages/ema/charts.md @@ -0,0 +1,3 @@ +# EMA Charts + +![](img/EMA10_Spike.svg) ![](img/EMA10_Impulse.svg) ![](img/EMA10_Triangle.svg) ![](img/EMA10_Sawtooth.svg) ![](img/EMA10_Sine.svg) ![](img/EMA10_Chirp.svg) ![](img/EMA10_White.svg) ![](img/EMA10_Gauss.svg) ![](img/EMA10_B.svg) ![](img/EMA10_HF.svg) ![](img/EMA10_ImpulseHF.svg) ![](img/EMA10_SawtoothHF.svg) ![](img/EMA10_SineG.svg) ![](img/EMA10_ChirpG.svg) ![](img/EMA10_Complex.svg) ![](img/EMA10_Market.svg) diff --git a/docs/indicators/averages/ema/ema.md b/docs/indicators/averages/ema/ema.md new file mode 100644 index 00000000..6b303acd --- /dev/null +++ b/docs/indicators/averages/ema/ema.md @@ -0,0 +1,4 @@ +## EMA: Exponential Moving Average + +The Exponential Moving Average (EMA) is one of the oldest statistical tools used in time series analysis, particularly in financial markets. It is a type of Infinite Impulse Response (IIR) filter that incorporates all past data into its calculation, albeit with exponentially decreasing weights. + diff --git a/docs/indicators/averages/ema/img/EMA10_B.svg b/docs/indicators/averages/ema/img/EMA10_B.svg new file mode 100644 index 00000000..b41c4506 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_B.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + B - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Chirp.svg b/docs/indicators/averages/ema/img/EMA10_Chirp.svg new file mode 100644 index 00000000..47172237 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Chirp.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Chirp - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_ChirpG.svg b/docs/indicators/averages/ema/img/EMA10_ChirpG.svg new file mode 100644 index 00000000..923d5b13 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_ChirpG.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Complex.svg b/docs/indicators/averages/ema/img/EMA10_Complex.svg new file mode 100644 index 00000000..b714fb89 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Complex.svg @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Gauss.svg b/docs/indicators/averages/ema/img/EMA10_Gauss.svg new file mode 100644 index 00000000..5539019a --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_HF.svg b/docs/indicators/averages/ema/img/EMA10_HF.svg new file mode 100644 index 00000000..d59b82dd --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_HF.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + HF - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Impulse.svg b/docs/indicators/averages/ema/img/EMA10_Impulse.svg new file mode 100644 index 00000000..703e1d87 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Impulse.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_ImpulseHF.svg b/docs/indicators/averages/ema/img/EMA10_ImpulseHF.svg new file mode 100644 index 00000000..ba2483f4 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Market.svg b/docs/indicators/averages/ema/img/EMA10_Market.svg new file mode 100644 index 00000000..5d7b41eb --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Sawtooth.svg b/docs/indicators/averages/ema/img/EMA10_Sawtooth.svg new file mode 100644 index 00000000..9db62420 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Sawtooth.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_SawtoothHF.svg b/docs/indicators/averages/ema/img/EMA10_SawtoothHF.svg new file mode 100644 index 00000000..867e705a --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_SawtoothHF.svg @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Sine.svg b/docs/indicators/averages/ema/img/EMA10_Sine.svg new file mode 100644 index 00000000..9abeb095 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Sine.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Sine - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_SineG.svg b/docs/indicators/averages/ema/img/EMA10_SineG.svg new file mode 100644 index 00000000..7aa35836 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_SineG.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Spike.svg b/docs/indicators/averages/ema/img/EMA10_Spike.svg new file mode 100644 index 00000000..9850ccba --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Spike.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_Triangle.svg b/docs/indicators/averages/ema/img/EMA10_Triangle.svg new file mode 100644 index 00000000..7c95c5b2 --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - EMA(10) + + diff --git a/docs/indicators/averages/ema/img/EMA10_White.svg b/docs/indicators/averages/ema/img/EMA10_White.svg new file mode 100644 index 00000000..f10da3cd --- /dev/null +++ b/docs/indicators/averages/ema/img/EMA10_White.svg @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - EMA(10) + + diff --git a/docs/indicators/averages/ema/quality.md b/docs/indicators/averages/ema/quality.md new file mode 100644 index 00000000..87a4a71c --- /dev/null +++ b/docs/indicators/averages/ema/quality.md @@ -0,0 +1,48 @@ +## EMA - Qualitative Ratings + +**Smoothness: 3/5** - EMA provides moderate smoothing. It's smoother than raw price data but less smooth than a Simple Moving Average (SMA) of the same period. The exponential weighting can sometimes allow short-term fluctuations to influence the average more than in an SMA. + +**Sensitivity: 4/5** - EMA can be quite sensitive to price changes, especially with high alpha values that prioritize more recent values. The exponential weighting means it reacts more quickly to new data compared to an SMA, making it more responsive to recent price movements and potential trend changes. + +**Overshooting: 5/5** - EMA, by its mathematical formulation, does not overshoot the latest price. It always falls between the most recent price and the previous EMA value. This property makes it excellent at avoiding overshooting, which is a significant advantage over some other types of moving averages that can extend beyond the range of actual prices. + +**Lag: 2/5** - EMA reduces lag compared to an SMA of the same period, but still introduces a very noticeable delay. While it responds more quickly to price changes than an SMA, it still lags behind the actual price movements, especially in rapidly trending markets or during significant reversals. + +### EMA - Key Characteristics + +- **IIR nature:** As an IIR filter, EMA's current value depends on **all** past inputs, theoretically extending the need for historical values back to infinity. IIR nature of EMA is a reason that EMA requires at least 1 - (1 - α)^N points to get to desired percentile of accuracy and be deemed 'warmed-up' for trading: +``` +EMA(5): 13 points to reach 95% accuracy +EMA(10): 27 ponts to reach 95% accuracy +EMA(20): 55 points to reach 95% accuracy +EMA(50): 138 points to reach 95% accuracy +EMA(100): 277 points to reach 95% accuracy +``` + +- **Weighted calculation:** Recent data points carry more significance, with weights decreasing exponentially for older data. The *period* number of bars represents only 86.5% of weights of calculated Ema. + +![EMA Weights](../../../img/emaweights.svg) + +- **Period parameter:** Period has no meaningful value beyond trivialization of calculating alpha. Weighting coefficient *alpha* is calculated from *period* as `alpha = 2/(period+1)`, making initial set of alphas: + +``` +Period (N) | Alpha (α) +-----------|----------- + 1 | 1.00 + 2 | 0.67 + 3 | 0.50 + 4 | 0.40 + 5 | 0.33 + 6 | 0.29 + 7 | 0.25 + 8 | 0.22 + 9 | 0.20 + 10 | 0.18 +``` + +But alpha factor can be any value between 1.00 and 0.00, not just discrete numbers calculated from Period. For fine-tuning trading strategies, avoid using period for any indicator in exponential (EMA) family. + +- **Reduced lag:** EMA exhibits less delay in reflecting trend changes compared to Simple Moving Average (SMA) as it weights more recent values progressively more. + + + diff --git a/docs/indicators/averages/sma/charts.dib b/docs/indicators/averages/sma/charts.dib new file mode 100644 index 00000000..dbdb13ba --- /dev/null +++ b/docs/indicators/averages/sma/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "SMA"; +int p = 10; +Func Indicator = period => new Sma(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/sma/charts.md b/docs/indicators/averages/sma/charts.md new file mode 100644 index 00000000..2130241b --- /dev/null +++ b/docs/indicators/averages/sma/charts.md @@ -0,0 +1,3 @@ +# SMA Charts + +![](img/SMA10_Spike.svg) ![](img/SMA10_Impulse.svg) ![](img/SMA10_Triangle.svg) ![](img/SMA10_Sawtooth.svg) ![](img/SMA10_Sine.svg) ![](img/SMA10_Chirp.svg) ![](img/SMA10_White.svg) ![](img/SMA10_Gauss.svg) ![](img/SMA10_B.svg) ![](img/SMA10_HF.svg) ![](img/SMA10_ImpulseHF.svg) ![](img/SMA10_SawtoothHF.svg) ![](img/SMA10_SineG.svg) ![](img/SMA10_ChirpG.svg) ![](img/SMA10_Complex.svg) ![](img/SMA10_Market.svg) diff --git a/docs/indicators/averages/sma/img/SMA10_B.svg b/docs/indicators/averages/sma/img/SMA10_B.svg new file mode 100644 index 00000000..a46f63de --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_B.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + B - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Chirp.svg b/docs/indicators/averages/sma/img/SMA10_Chirp.svg new file mode 100644 index 00000000..c7e76419 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Chirp.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Chirp - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_ChirpG.svg b/docs/indicators/averages/sma/img/SMA10_ChirpG.svg new file mode 100644 index 00000000..9e5f5029 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_ChirpG.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Complex.svg b/docs/indicators/averages/sma/img/SMA10_Complex.svg new file mode 100644 index 00000000..b6d1bd9c --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Complex.svg @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Gauss.svg b/docs/indicators/averages/sma/img/SMA10_Gauss.svg new file mode 100644 index 00000000..e965054b --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_HF.svg b/docs/indicators/averages/sma/img/SMA10_HF.svg new file mode 100644 index 00000000..25035a25 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_HF.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + HF - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Impulse.svg b/docs/indicators/averages/sma/img/SMA10_Impulse.svg new file mode 100644 index 00000000..3de92634 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Impulse.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_ImpulseHF.svg b/docs/indicators/averages/sma/img/SMA10_ImpulseHF.svg new file mode 100644 index 00000000..3c6a8507 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Market.svg b/docs/indicators/averages/sma/img/SMA10_Market.svg new file mode 100644 index 00000000..741aac20 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Sawtooth.svg b/docs/indicators/averages/sma/img/SMA10_Sawtooth.svg new file mode 100644 index 00000000..e45ddf26 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Sawtooth.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_SawtoothHF.svg b/docs/indicators/averages/sma/img/SMA10_SawtoothHF.svg new file mode 100644 index 00000000..aa6fdc64 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_SawtoothHF.svg @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Sine.svg b/docs/indicators/averages/sma/img/SMA10_Sine.svg new file mode 100644 index 00000000..a69849cc --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Sine.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + Sine - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_SineG.svg b/docs/indicators/averages/sma/img/SMA10_SineG.svg new file mode 100644 index 00000000..db73aa27 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_SineG.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Spike.svg b/docs/indicators/averages/sma/img/SMA10_Spike.svg new file mode 100644 index 00000000..c3c45a07 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Spike.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_Triangle.svg b/docs/indicators/averages/sma/img/SMA10_Triangle.svg new file mode 100644 index 00000000..4bc032ff --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - SMA(10) + + diff --git a/docs/indicators/averages/sma/img/SMA10_White.svg b/docs/indicators/averages/sma/img/SMA10_White.svg new file mode 100644 index 00000000..992e0c05 --- /dev/null +++ b/docs/indicators/averages/sma/img/SMA10_White.svg @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - SMA(10) + + diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md new file mode 100644 index 00000000..dbe9e221 --- /dev/null +++ b/docs/indicators/indicators.md @@ -0,0 +1,140 @@ +# Indicators in QuanTAlib + +⭐= Validation against several TA libraries
+✔️= Validation tests passed
+❌= Issue + +|**BASIC TRANSFORMS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| +|--|:--:|:--:|:--:|:--:|:--:| +|OC2 - Midpoint price|️`.OC2`|`CandlePart.OC2`|`MidPoint`|| +|HL2 - Median Price|️`.HL2`|`CandlePart.HL2`|`MedPrice`|| +|HLC3 - Typical Price|️`.HLC3`|`CandlePart.HLC3`|`TypPrice`|| +|OHL3 - Mean Price|`️.OHL3`|`CandlePart.OHL3`||| +|OHLC4 - Average Price|`️.OHLC4`|`CandlePart.OHLC4`|`AvgPrice`|| +|HLCC4 - Weighted Price|`️.HLCC4`||`WclPrice`|| +|
|||| +|**AVERAGES & TRENDS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| +|AFIRMA - Autoregressive Finite Impulse Response Moving Average||||| +|ALMA - Arnaud Legoux Moving Average|`Alma`|`✔️`||| +|⭐DEMA - Double EMA Average|`Dema`|`⭐`|`⭐`|`⭐`|| +|DSMA - Deviation Scaled Moving Average|`Dsma`|||| +|DWMA - Double WMA Average|`Dwma`|||| +|⭐EMA - Exponential Moving Average|[`Ema`](indicators/averages/ema/ema.md)|`⭐`|`⭐`|`⭐`|`⭐`| +|EPMA - Endpoint Moving Average|`Epma`|`✔️`||| +|FRAMA - Fractal Adaptive Moving Average|`Frama`|||| +|FWMA - Fibonacci Weighted Moving Average|`Fwma`|||| +|HILO - Gann High-Low Activator||||| +|HTIT - Hilbert Transform Instantaneous Trendline|`Htit`|`✔️`|`✔️`|| +|GMA - Gaussian-Weighted Moving Average|`Gma`|||| +|HMA - Hull Moving Average|`Hma`|`✔️`||`✔️`| +|HWMA - Holt-Winter Moving Average|`Hwma`|||| +|JMA - Jurik Moving Average|`Jma`|||| +|KAMA - Kaufman's Adaptive Moving Average|`Kama`|`✔️`|`✔️`|`✔️`| +|KDJ - KDJ Indicator (trend reversal)||||| +|LTMA - Laguerre Transform Moving Average|`Ltma`|||| +|MAAF - Median-Average Adaptive Filter|`Maaf`|||| +|MACD - Movign Average Convergence/Divergence||`✔️`|`✔️`|| +|MAMA - MESA Adaptive Moving Average|`Mama`|`✔️`|`✔️`|| +|MGDI - McGinley Dynamic Indicator|`Mgdi`|`✔️`||| +|MMA - Modified Moving Average|`Mma`|||| +|PPMA - Pivot Point Moving Average||||| +|PWMA - Pascal's Weighted Moving Average||||| +|QEMA - Quad Exponential Moving Average|`Qema`|||| +|RMA - WildeR's Moving Average|`Rma`|||| +|SINEMA - Sine Weighted Moving Average|`Sinema`|||| +|⭐SMA - Simple Moving Average|[`Sma`](indicators/averages/sma/sma.md)|`⭐`|`⭐`|`⭐`|`⭐`| +|SMMA - Smoothed Moving Average|`Smma`|`✔️`||| +|SSF - Ehler's Super Smoother Filter||||| +|SUPERTREND - Supertrend||`✔️`||| +|SWMA - Symmetric Weighted Moving Average||||| +|T3 - Tillson T3 Moving Average|`T3`|`✔️`|`✔️`|| +|TEMA - Triple EMA Average|`Tema`|`✔️`|`✔️`|`✔️`| +|TRIMA - Triangular Moving Average|`Trima`|`✔️`||`✔️`| +|TSF - Time Series Forecast|||`✔️`|`✔️`| +|VIDYA - Variable Index Dynamic Average|`Vidya`|||`✔️`| +|VORTEX - Vortex Indicator||`✔️`||| +|WMA - Weighted Moving Average|`Wma`|`✔️`|`✔️`|`✔️`| +|ZLEMA - Zero Lag EMA Average|`Zlema`|||`✔️`| +|
|||| +|**VOLATILITY INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| +|ADL - Chaikin Accumulation Distribution Line||`GetAdl`|`Ad`|| +|ADOSC - Chaikin Accumulation Distribution Oscillator||`GetChaikinOsc`|`AdOsc`|| +|ATR - Average True Range||`GetAtr`|`Atr`|| +|ATRP - Average True Range Percent||||| +|ATRSTOP - ATR Trailing Stop ||`GetAtrStop`||| +|BETA - Beta coefficient||||| +|BBANDS - Bollinger Bands®||`BollingerBands`||| +|CHAND - Chandelier Exit||`GetChandelier`||| +|CRSI - Connor RSI||`GetConnorsRsi`||| +|CVI - Chaikins Volatility||||| +|DON - Donchian Channels||`GetDonchian`||| +|FCB - Fractal Chaos Bands||`GetFcb`||| +|FISHER - Fisher Transform||||| +|HV - Historical Volatility||||| +|ICH - Ichimoku Cloud||`GetIchimoku`||| +|KEL - Keltner Channels||`GetKeltner`||| +|NATR - Normalized Average True Range||`GetAtr`||| +|CHN - Price Channel Indicator||||| +|RSI - Relative Strength Index||`GetRsi`||| +|SAR - Parabolic Stop and Reverse||`GetParabolicSar`||| +|SRSI - Stochastic RSI||`GetStochRsi`||| +|STARC - Starc Bands||`GetStarcBands`||| +|TR - True Range||||| +|UI - Ulcer Index||`GetUlcerIndex`||| +|VSTOP - Volatility Stop||`GetVolatilityStop`||| +|
|||| +|**MOMENTUM INDICATORS & OSCILLATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| +|AC - Acceleration Oscillator||||| +|ADX - Average Directional Movement Index||`GetAdx`|`Adx`|| +|ADXR - Average Directional Movement Index|| `Rating`|`Adxr`|| +|AO - Awesome Oscillator||`GetAwesome`||| +|APO - Absolute Price Oscillator||`Apo`||| +|AROON - Aroon oscillator||`GetAroon`|`Aroon`|| +|BOP - Balance of Power||`GetBop`|`Bop`|| +|CCI - Commodity Channel Index||`GetCci`|`Cci`|| +|CFO - Chande Forcast Oscillator||||| +|CMO - Chande Momentum Oscillator||`GetCmo`|`Cmo`|| +|CHOP - Choppiness Index||`GetChop`||| +|COG - Center of Gravity||||| +|COPPOCK - Coppock Curve||||| +|CTI - Ehler's Correlation Trend Indicator||||| +|DPO - Detrended Price Oscillator||`GetDpo`||| +|DMI - Directional Movement Index||`GetDmi`||| +|EFI - Elder Ray's Force Index||`GetElderRay`||| +|FOSC - Forecast oscillator|||||| +|GATOR - Gator oscillator||`GetGator`||| +|HURST - Hurst Exponent||`GetHurst`||| +|KRI - Kairi Relative Index||||| +|KVO - Klinger Volume Oscillator||`GetKvo`|||| +|MFI - Money Flow Index||`GetMfi`||| +|MOM - Momentum||||| +|NVI - Negative Volume Index||||| +|PO - Price Oscillator||||| +|PPO - Percentage Price Oscillator||||| +|PMO - Price Momentum Oscillator||`GetPmo`||| +|PVI - Positive Volume Index||||| +|ROC - Rate of Change||GetRoc||| +|RVGI - Relative Vigor Index||||| +|SMI - Stochastic Momentum Index||`GetSmi`||| +|STC - Schaff Trend Cycle||`GetStc`||| +|STOCH - Stochastic Oscillator||`GetStoch`||| +|TRIX - 1-day ROC of TEMA||`GetTrix`||`trix.Run`| +|TSI - True Strength Index||`GetTsi`||| +|UO - Ultimate Oscillator||`GetUltimate`||| +|WILLR - Larry Williams' %R||GetWillia`msR`||| +|WGAT - Williams Alligator||`GetAlligator`||| +|
|||| +|**VOLUME INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| +|AOBV - Archer On-Balance Volume||||| +|CMF - Chaikin Money Flow||`GetCmf`||| +|EOM - Ease of Movement||||| +|KVO - Klinger Volume Oscilaltor||||| +|OBV - On-Balance Volume||`GetObv`||| +|PRS - Price Relative Strength||`GetPrs`||| +|PVOL - Price-Volume||||| +|PVO - Percentage Volume Oscillator||`GetPvo`||| +|PVR - Price Volume Rank||||| +|PVT - Price Volume Trend||||| +|VP - Volume Profile||||| +|VWAP - Volume Weighted Average Price||`GetVwap`||| +|VWMA - Volume Weighted Moving Average||`GetVwma`|||| diff --git a/docs/indicators/statistics/list.md b/docs/indicators/statistics/list.md new file mode 100644 index 00000000..77166046 --- /dev/null +++ b/docs/indicators/statistics/list.md @@ -0,0 +1,52 @@ +# Top 50 Fundamental Statistical Functions + +- Mean (Arithmetic Average) +- Median +- Mode +- Range +- Variance +- Standard Deviation +- Coefficient of Variation +- Percentiles +- Quartiles +- Interquartile Range (IQR) +- Skewness +- Kurtosis +- Covariance +- Correlation Coefficient (Pearson) +- Spearman's Rank Correlation +- Mean Absolute Error (MAE) +- Mean Squared Error (MSE) +- Root Mean Square Error (RMSE) +- Mean Absolute Percentage Error (MAPE) +- R-squared (Coefficient of Determination) +- Adjusted R-squared +- Z-score (Standard Score) +- T-score +- F-statistic +- Chi-square Statistic +- P-value +- Confidence Interval +- Standard Error +- Margin of Error +- Effect Size (Cohen's d, Hedges' g) +- Odds Ratio +- Relative Risk +- Poisson Distribution +- Normal Distribution (Gaussian) +- Binomial Distribution +- Exponential Distribution +- Weibull Distribution +- Log-normal Distribution +- Student's t-Distribution +- F-Distribution +- Beta Distribution +- Gamma Distribution +- Geometric Mean +- Harmonic Mean +- Moving Average +- Exponential Moving Average +- Weighted Average +- Cumulative Sum +- Autocorrelation Function (ACF) +- Partial Autocorrelation Function (PACF) \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md index 226ee7b8..2184218a 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,4 +1,6 @@ -# QuanTAlib - quantitative technical indicators for Quantower and other C#-based trading platorms +# QuanTAlib - quantitative technical indicators for Quantower + +## (and other C#-based trading platorms) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=mihakralj_QuanTAlib&metric=ncloc)](https://sonarcloud.io/summary/overall?id=mihakralj_QuanTAlib) [![Codacy grade](https://img.shields.io/codacy/grade/b1f9109222234c87bce45f1fd4c63aee?style=flat-square)](https://app.codacy.com/gh/mihakralj/QuanTAlib/dashboard) @@ -10,25 +12,23 @@ ![GitHub last commit](https://img.shields.io/github/last-commit/mihakralj/QuanTAlib) [![Nuget](https://img.shields.io/nuget/dt/QuanTAlib?style=flat-square)](https://www.nuget.org/packages/QuanTAlib/) [![GitHub watchers](https://img.shields.io/github/watchers/mihakralj/QuanTAlib?style=flat-square)](https://github.com/mihakralj/QuanTAlib/watchers) -[![.NET7.0](https://img.shields.io/badge/.NET-7.0%20%7C%206.0%20%7C%204.8-blue?style=flat-square)](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) +[![.NET8.0](https://img.shields.io/badge/.NET-8.0-blue?style=flat-square)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) **Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for analyzing quotes with [Quantower](https://www.quantower.com/) and other C#-based trading platforms. **QuanTAlib** is written with some specific design criteria in mind - why there is '_yet another C# TA library_': -- Prioritize **real-time data analysis** (series can add new data and indicator doesn't have to re-calculate the whole history) -- **Allow updates** to the last quote and adjusting the calculation to the still-forming bar -- **Calculate early data right** - output data is as valid as mathematically possible from the first value onwards +- Prioritize **real-time data analysis**: As new data items arrives, indicators don't have to re-calculate the entire history and can generate a result directly from the last item +- **Allow updates/corrections** of the last quote - QuanTAlib is re-calculating the last value as many times as required before continuing to the new bar +- **Calculate early data right** - calculated data is as valid as mathematically possible from the first value onwards - no blackout or warming-up periods. All indicators return data from the first bar, alongside with a flag `isHot` - defining if calculation is already stable. ![Alt text](./img/quotes.gif) -If not obvious, QuanTAlib is intended for developers, and it does not focus on sources of OHLCV quotes. There are some very basic data feeds available to use in the learning process: `RND_Feed` and `GBM_Feed` for random data, `Yahoo_Feed` and `Alphavantage_Feed` for a quick grab of daily data of US stock market. - -See [Getting Started](https://github.com/mihakralj/QuanTAlib/blob/main/Docs/getting_started.ipynb) .NET interactive notebook to get a feel how library works. Developers can use QuanTAlib in [Polyglot Notebooks](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) or in console apps, but the best usage of the library is with C#-enabled trading platforms - see **QuanTower_Charts** folder for Quantower examples and check **Releases** for compiled Quantower DLL. +QuanTAlib is intended for developers and users of Quantower, therefore it does not focus on privind sources of OHLCV quotes. There are some very basic data feeds available to use in the learning process: `GBM_Feed` for Random (Geometric Brownian Motion) data, and `SyntheticVendor` data generator for Quantower. ### Coverage -[List of all indicators - current and planned](indicators.md) +[List of indicators - implemented and planned](indicators/indicators.md) ### Validation @@ -36,9 +36,5 @@ QuanTAlib uses validation tests with four other TA libraries to assure accuracy - [TA-LIB](https://www.ta-lib.org/function.html) - [Skender Stock Indicators](https://dotnet.stockindicators.dev/) -- [Pandas-TA](https://twopirllc.github.io/pandas-ta/) - [Tulip Indicators](https://tulipindicators.org/) -### Questions - -[Some most common questions addressed](QA.md) \ No newline at end of file diff --git a/docs/setup/dotpeek.md b/docs/setup/dotpeek.md new file mode 100644 index 00000000..b9ccb5d6 --- /dev/null +++ b/docs/setup/dotpeek.md @@ -0,0 +1,46 @@ +# Unearthing Quantower Secrets with dotPeek + +## Step 1: Acquire Your Digital Pickaxe (dotPeek) + +1. Visit JetBrains' dotPeek download page: https://www.jetbrains.com/decompiler/download/ +2. Click the **Download** button (it's big and blue, kinda hard to miss) +3. Once downloaded, run the installer + +## Step 2: Fire Up dotPeek + +1. Launch dotPeek (look for the DP icon ) +2. Marvel at its sleek interface from early 2000's (but who needs fancy UI, right?) + +## Step 3: Load the *TradingPlatform.BusinessLayer.dll* + +1. File > Open > Navigate to your Quantower installation folder, like `D:\Quantower\TradingPlatform\v1.140.14\bin` +2. Find and open `TradingPlatform.BusinessLayer.dll` +4. Watch as dotPeek works its magic, decompiling the assembly + +## Step 4: Uncover the Secrets + +1. Expand the assembly tree in the left pane and look for interesting namespaces and classes + - `\Scripts\Indicators\Moving Averages\` is a good starting point + +3. Double-click on classes to view their decompiled source +4. Pay attention to: + - Public and internal classes/methods + - Interfaces and abstract classes + - Attributes and custom annotations + - Hardcoded values and constants + +## Step 5: Document Your Findings + +1. Use dotPeek's "Save All" feature to export the decompiled source +2. Create a document to note interesting discoveries and stuff that Quantower documentation didn't tell you +3. Some parts of Quantower are obfuscated. Which is funny, in the era of generative AI and easy-peasy de-obfuscation ability. + +## Ethical Considerations + +Remember, with great power comes great responsibility: +- Respect Quantower's intellectual property +- Consider contributing to Quantower's documentation if you find features worth documenting + +Happy exploring! May your code be clean and your discoveries plentiful. + +![dotpeek](../img/dotpeek.png) \ No newline at end of file diff --git a/docs/setup/vscode.md b/docs/setup/vscode.md new file mode 100644 index 00000000..85248d3e --- /dev/null +++ b/docs/setup/vscode.md @@ -0,0 +1,89 @@ +# Development Environment Setup in VS Code + +Call me grizzled old man, but I do not like to use [full Visual Studio](https://help.quantower.com/quantower/quantower-algo/installing-visual-studio) environment for my coding work. Here is the setup for VS Code projects for Quantower, so you can build your own as well. + +### Prerequisites + +- [VS Code](https://code.visualstudio.com/) - obviously +- [.NET SDK](https://dotnet.microsoft.com/en-us/download) - you should probably have this already +- [C# Dev Kit Extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) - so VS Code can understand C# +- [C# Base language support Extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) - I *think* this is a prereq for C# Dev Kit and will install automatically +- [Polyglot Notebooks Extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) - optional, but really recommended for tinkering with C# code + +### Installation Steps + +1. Create a new `myIndicator.csproj` file in a directory of your choice - it doesn't have to be anywhere in Quantower directory structure +2. Add all standard elements to `myIndicator.csproj` +3. We need to tell dotnet compiler how to find `TradingPlatform.BusinessLayer.dll` assembly. it is hiding deep in the bowels of Quantower directory structure, including an ever-changing version directory. Luckily msbuild magick can help: + +``` XML + + + D:\Quantower + + $([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0]) + + + + + $(QuantowerPath)\bin\TradingPlatform.BusinessLayer.dll + + + + TradingPlatform.BusinessLayer.xml + + +``` +4. Each time dotnet compiler creates a new dll assembly, we need to copy it to the `.\Scripts\Indicatiors` directory so Quantower can use it. Let's automate this with a post-build event in our `myIndicator.csproj`: + +``` xml + + + + +``` + +Below is a sample complete `.csproj` file for a Quantower indicator - it should allow building the .dll assembly and copying it to Quantower structure with `dotnet build` command: + +``` xml + + + en-US + net8.0 + enable + enable + true + preview + false + false + true + AnyCPU + False + bin\$(Configuration)\ + False + full + true + true + true + snupkg + AnyCPU + + + + D:\Quantower + $([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0]) + + + + $(QuantowerPath)\bin\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + + + + + + + +``` \ No newline at end of file diff --git a/lib/averages/Alma.cs b/lib/averages/Alma.cs new file mode 100644 index 00000000..b884bc4b --- /dev/null +++ b/lib/averages/Alma.cs @@ -0,0 +1,121 @@ + +using System.Runtime.CompilerServices; +namespace QuanTAlib; + +/// +/// ALMA: Arnaud Legoux Moving Average +/// Uses the curve of the Normal (Gauss) distribution. This moving average reduces lag +/// of the data in conjunction with smoothing to reduce noise. +/// +/// +/// Smoothness: ★★★★☆ (4/5) +/// Sensitivity: ★★★★☆ (4/5) +/// Overshooting: ★★★★☆ (4/5) +/// Lag: ★★★★★ (5/5) +/// +/// Validation: +/// Skender.Stock.Indicators +/// + +public class Alma : AbstractBase +{ + private readonly int _period; + private readonly double _offset; + private readonly double _sigma; + private CircularBuffer? _buffer; + private CircularBuffer? _weight; + private double _norm; + + /// The number of data points used in the ALMA calculation. + /// Controls the smoothness and high-frequency filtering. Default is 0.85. + /// Controls the shape of the Gaussian distribution. Default is 6. + /// Thrown when period is less than 1. + public Alma(int period, double offset = 0.85, double sigma = 6) : base() + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _offset = offset; + _sigma = sigma; + WarmupPeriod = period; + Name = "Alma"; + Init(); + } + + /// The data source object that publishes updates. + /// The number of data points used in the ALMA calculation. + /// Controls the smoothness and high-frequency filtering. Default is 0.85. + /// Controls the shape of the Gaussian distribution. Default is 6. + public Alma(object source, int period, double offset = 0.85, double sigma = 6) : this(period, offset, sigma) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer = new CircularBuffer(_period); + _weight = new CircularBuffer(_period); + _norm = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + } + } + + /// + /// Performs the core ALMA calculation. Called from parent abstractBase Calc() + /// + /// The calculated ALMA value. + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer!.Add(Input.Value, Input.IsNew); + if (_weight!.Count < _buffer.Count) + { + for (var i = 0; i < _buffer.Count - _weight.Count; i++) + { + _weight.Add(0.0); + } + } + + if (_buffer.Count <= _period) + { + UpdateWeights(); + } + + double weightedSum = 0; + for (var i = 0; i < _buffer.Count; i++) + { + weightedSum += _weight[i] * _buffer[i]; + } + + double result = weightedSum / _norm; + + IsHot = _index >= WarmupPeriod; + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateWeights() + { + int len = _buffer!.Count; + _norm = 0; + double m = _offset * (len - 1); + double s = len / _sigma; + for (int i = 0; i < len; i++) + { + double wt = Math.Exp(-((i - m) * (i - m)) / (2 * s * s)); + _weight![i] = wt; + _norm += wt; + } + } +} \ No newline at end of file diff --git a/lib/averages/Convolution.cs b/lib/averages/Convolution.cs new file mode 100644 index 00000000..470f426c --- /dev/null +++ b/lib/averages/Convolution.cs @@ -0,0 +1,103 @@ +namespace QuanTAlib; + +public class Convolution : AbstractBase +{ + private readonly double[] _kernel; + private readonly int _kernelSize; + private CircularBuffer _buffer; + private double[] _normalizedKernel; + + public Convolution(double[] kernel) + { + if (kernel == null || kernel.Length == 0) + { + throw new ArgumentException("Kernel must not be null or empty.", nameof(kernel)); + } + _kernel = kernel; + _kernelSize = kernel.Length; + _buffer = new CircularBuffer(_kernelSize); + _normalizedKernel = new double[_kernelSize]; + Init(); + } + + public Convolution(object source, double[] kernel) : this(kernel) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private new void Init() + { + base.Init(); + _buffer.Clear(); + Array.Copy(_kernel, _normalizedKernel, _kernelSize); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double GetLastValid() + { + return _lastValidValue; + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + // Normalize kernel on each calculation until buffer is full + if (_index <= _kernelSize) + { + NormalizeKernel(); + } + + double result = ConvolveBuffer(); + IsHot = _index >= _kernelSize; + + return result; + } + + private void NormalizeKernel() + { + int activeLength = Math.Min(_index, _kernelSize); + double sum = 0; + + // Calculate the sum of the active kernel elements + for (int i = 0; i < activeLength; i++) + { + sum += _kernel[i]; + } + + // Normalize the kernel or set equal weights if the sum is zero + double normalizationFactor = (sum != 0) ? sum : activeLength; + for (int i = 0; i < activeLength; i++) + { + _normalizedKernel[i] = _kernel[i] / normalizationFactor; + } + + // Set the rest of the normalized kernel to zero + Array.Clear(_normalizedKernel, activeLength, _kernelSize - activeLength); + } + + private double ConvolveBuffer() + { + double sum = 0; + var bufferSpan = _buffer.GetSpan(); + int activeLength = Math.Min(_index, _kernelSize); + + for (int i = 0; i < activeLength; i++) + { + sum += bufferSpan[activeLength - 1 - i] * _normalizedKernel[i]; + } + + return sum; + } +} \ No newline at end of file diff --git a/lib/averages/Dema.cs b/lib/averages/Dema.cs new file mode 100644 index 00000000..afd57c81 --- /dev/null +++ b/lib/averages/Dema.cs @@ -0,0 +1,105 @@ +namespace QuanTAlib; + +/// +/// DEMA: Double Exponential Moving Average +/// DEMA reduces the lag of a traditional EMA by applying a second EMA over EMA. +/// It responds more quickly to price changes than a standard EMA while maintaining +/// smoothness, at the cost of overshooting the signal line. +/// +/// +/// Smoothness: ★★★☆☆ (3/5) +/// Sensitivity: ★★★★☆ (4/5) +/// Overshooting: ★★★☆☆ (3/5) +/// Lag: ★★★★☆ (4/5) +/// +/// Sources: +/// https://www.investopedia.com/terms/d/double-exponential-moving-average.asp +/// https://www.tradingview.com/support/solutions/43000502589-double-exponential-moving-average-dema/ +/// +/// Validation: +/// Skender.Stock.Indicators +/// +public class Dema : AbstractBase +{ + // inherited _index + // inherited _value + private readonly int _period; + private double _lastEma1, _p_lastEma1; + private double _lastEma2, _p_lastEma2; + private double _k, _e, _p_e; + + public Dema(int period) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _period = period; + Name = "Dema"; + double percentile = 0.85; //targeting 85th percentile of correctness of converging EMA + WarmupPeriod = (int)Math.Ceiling(-period * Math.Log(1 - percentile)); + Init(); + } + + public Dema(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + //inhereted public void Sub(object source, in ValueEventArgs args) + + public override void Init() + { + base.Init(); + _k = 2.0 / (_period + 1); + _e = 1.0; + _lastEma1 = 0; + _lastEma2 = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_lastEma1 = _lastEma1; + _p_lastEma2 = _lastEma2; + _p_e = _e; + _index++; + } + else + { + _lastEma1 = _p_lastEma1; + _lastEma2 = _p_lastEma2; + _e = _p_e; + } + } + + /// + /// Core DEMA calculation + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double result, _ema1, _ema2; + // compensator for early ema values + _e = (_e > 1e-10) ? (1 - _k) * _e : 0; + double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1; + + // Calculate EMA1 + _ema1 = _k * (Input.Value - _lastEma1) + _lastEma1; + + // Calculate EMA2 using compensatedEma1 + _ema2 = _k * (_ema1 * _invE - _lastEma2) + _lastEma2; + + // Calculate DEMA + double _dema = 2 * _ema1 * _invE - (_ema2 * _invE); + + result = _dema; + _lastEma1 = _ema1; + _lastEma2 = _ema2; + + IsHot = _index >= WarmupPeriod; + return result; + } +} diff --git a/lib/averages/Dsma.cs b/lib/averages/Dsma.cs new file mode 100644 index 00000000..ed684e96 --- /dev/null +++ b/lib/averages/Dsma.cs @@ -0,0 +1,144 @@ +namespace QuanTAlib; + +/// +/// DSMA: Deviation Scaled Moving Average +/// Adaptive moving average that adjusts its smoothing factor based on the volatility of the input data. +/// It aims to be more responsive during trending periods and more stable during ranging periods. +/// +/// +/// Smoothness: ★★★★☆ (4/5) +/// Sensitivity: ★★★★☆ (4/5) +/// Overshooting: ★★★★☆ (4/5) +/// Lag: ★★★★☆ (4/5) +/// +/// The DSMA uses a SuperSmoother filter to reduce noise and a dynamic alpha calculation based on the +/// scaled deviation of the input data. This allows it to adapt to changing market conditions. +/// +/// The algorithm involves these main steps: +/// 1. Apply a SuperSmoother filter to the zero-mean input data. +/// 2. Calculate the Root Mean Square (RMS) of the filtered data. +/// 3. Scale the filtered data by the RMS to get a measure in terms of standard deviations. +/// 4. Use the scaled deviation to calculate an adaptive alpha for the moving average. +/// +/// Source: +/// https://www.mesasoftware.com/papers/DEVIATION%20SCALED%20MOVING%20AVERAGE.pdf +/// + +public class Dsma : AbstractBase +{ + private readonly int _period; + private readonly CircularBuffer _buffer; + private readonly double _a1, _b1, _c1, _c2, _c3; + private double _lastDsma, _p_lastDsma; + private double _filt, _filt1, _filt2, _zeros, _zeros1; + private double _p_filt, _p_filt1, _p_filt2, _p_zeros, _p_zeros1; + private bool _isInit, _p_isInit; + + /// + /// Initializes a new instance of the class. + /// + /// The number of data points used in the DSMA calculation. + /// Thrown when period is less than 1. + public Dsma(int period) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _period = period; + _buffer = new CircularBuffer(period); + + // SuperSmoother filter coefficients + _a1 = Math.Exp(-1.414 * Math.PI / (0.5 * period)); + _b1 = 2 * _a1 * Math.Cos(1.414 * Math.PI / (0.5 * period)); + _c2 = _b1; + _c3 = -_a1 * _a1; + _c1 = 1 - _c2 - _c3; + + Name = "Dsma"; + WarmupPeriod = period * 2; // A conservative estimate + Init(); + } + + public Dsma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _lastDsma = 0; + _filt = _filt1 = _filt2 = 0; + _zeros = _zeros1 = 0; + _isInit = false; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_lastDsma = _lastDsma; + _p_isInit = _isInit; + _p_zeros = _zeros; + _p_zeros1 = _zeros1; + _p_filt = _filt; + _p_filt1 = _filt1; + _p_filt2 = _filt2; + _index++; + } + else + { + _lastDsma = _p_lastDsma; + _isInit = _p_isInit; + _zeros = _p_zeros; + _zeros1 = _p_zeros1; + _filt = _p_filt; + _filt1 = _p_filt1; + _filt2 = _p_filt2; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + if (!_isInit) + { + _lastDsma = Input.Value; + _isInit = true; + return _lastDsma; + } + + // Produce nominal zero mean + _zeros = Input.Value - _lastDsma; + + // SuperSmoother Filter + _filt = _c1 * (_zeros + _zeros1) / 2 + _c2 * _filt1 + _c3 * _filt2; + + // Update buffer for RMS calculation + _buffer.Add(_filt * _filt, Input.IsNew); + + // Compute RMS (Root Mean Square) + double rms = Math.Sqrt(_buffer.Sum() / _period); + + // Rescale Filt in terms of Standard Deviations + double scaledFilt = rms != 0 ? _filt / rms : 0; + + // Calculate adaptive alpha + double alpha = Math.Abs(scaledFilt) * 5 / _period; + + // DSMA calculation + double dsma = alpha * Input.Value + (1 - alpha) * _lastDsma; + + // Update state variables + _zeros1 = _zeros; + _filt2 = _filt1; + _filt1 = _filt; + _lastDsma = dsma; + + IsHot = _index >= WarmupPeriod; + return dsma; + } +} diff --git a/lib/averages/Dwma.cs b/lib/averages/Dwma.cs new file mode 100644 index 00000000..ec7fc883 --- /dev/null +++ b/lib/averages/Dwma.cs @@ -0,0 +1,86 @@ +namespace QuanTAlib; + +/// +/// DWMA: Double Weighted Moving Average +/// DWMA is a technical indicator that applies a Weighted Moving Average (WMA) twice to the input data. +/// The weights are decreasing over the period with p^2 decay, and the most recent data has the heaviest weight. +/// +/// +/// Smoothness: ★★★★★ (5/5) +/// Sensitivity: ★★★☆☆ (3/5) +/// Overshooting: ★★★★☆ (4/5) +/// Lag: ★★☆☆☆ (2/5) +/// +/// The DWMA is calculated by applying two WMAs in sequence: +/// 1. An inner WMA is applied to the input data. +/// 2. An outer WMA is then applied to the result of the inner WMA. +/// +/// Key characteristics: +/// - The weight distribution follows a p^2 decay, where p is the position of the data point. +/// - More recent data points receive higher weights, emphasizing recent price movements. +/// - The double application of WMA results in a smoother indicator compared to a single WMA. +/// +/// The formula for DWMA can be expressed as: +/// DWMA = WMA(WMA(price, period), period) +/// +/// Where WMA is the Weighted Moving Average function and 'period' is the number of data points used in each WMA calculation. +/// + +public class Dwma : AbstractBase +{ + private readonly int _period; + private readonly Wma _innerWma; + private readonly Wma _outerWma; + + public Dwma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _innerWma = new Wma(period); + _outerWma = new Wma(period); + Name = "Wma"; + WarmupPeriod = 2 * _period - 1; + Init(); + } + + public Dwma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _innerWma.Init(); + _outerWma.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Calculate inner WMA + TValue innerResult = _innerWma.Calc(Input); + + // Calculate outer WMA using the result of inner WMA + TValue outerResult = _outerWma.Calc(innerResult); + + double result = outerResult.Value; + IsHot = _index >= WarmupPeriod; + + return result; + } +} diff --git a/lib/averages/Ema.cs b/lib/averages/Ema.cs new file mode 100644 index 00000000..7e306631 --- /dev/null +++ b/lib/averages/Ema.cs @@ -0,0 +1,130 @@ +namespace QuanTAlib; + +/// +/// EMA: Exponential Moving Average +/// EMA needs very short history buffer and calculates the EMA value using just the +/// previous EMA value. The weight of the new datapoint (alpha) is alpha = 2 / (period + 1) +/// +/// +/// Smoothness: ★★★☆☆ (3/5) +/// Sensitivity: ★★★★☆ (4/5) +/// Overshooting: ★★★★★ (5/5) +/// Lag: ★★★☆☆ (3/5) +/// +/// Key characteristics: +/// - Uses no buffer, relying only on the previous EMA value. +/// - The weight of new data points is calculated as alpha = 2 / (period + 1). +/// - Provides a balance between responsiveness and smoothing. No overshooting. Significant lag +/// +/// Calculation method: +/// This implementation can use SMA for the first Period bars as a seeding value for EMA when useSma is true. +/// +/// Sources: +/// - https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages +/// - https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp +/// - https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA +/// + +public class Ema : AbstractBase +{ + // inherited _index + // inherited _value + private readonly int _period; + private CircularBuffer _sma; + private double _lastEma, _p_lastEma; + private double _k, _e, _p_e; + private bool _isInit, _p_isInit, _useSma; + + public Ema(int period, bool useSma = true) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _period = period; + _k = 2.0 / (_period + 1); + _useSma = useSma; + _sma = new(period); + Name = "Ema"; + WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - _k)); //95th percentile + Init(); + } + + public Ema(double alpha) : base() + { + _k = alpha; + _useSma = false; + _sma = new(1); + _period = 1; + WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - _k)); //95th percentile + Init(); + } + + public Ema(object source, int period, bool useSma = true) : this(period, useSma) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + //inhereted public void Sub(object source, in ValueEventArgs args) + + public override void Init() + { + base.Init(); + _e = 1.0; + _lastEma = 0; + _isInit = false; + _p_isInit = false; + _sma = new(_period); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_lastEma = _lastEma; + _p_isInit = _isInit; + _p_e = _e; + _index++; + } + else + { + _lastEma = _p_lastEma; + _isInit = _p_isInit; + _e = _p_e; + } + } + + /// + /// Core EMA calculation + /// + protected override double Calculation() + { + double result, _ema; + ManageState(Input.IsNew); + + // when _UseSma == true, use SMA calculation until we have enough data points + if (!_isInit && _useSma) + { + _sma.Add(Input.Value, Input.IsNew); + _ema = _sma.Average(); + result = _ema; + if (_index >= _period) + { + _isInit = true; + } + } + else + { + // compensator for early ema values + _e = (_e > 1e-10) ? (1 - _k) * _e : 0; + + _ema = _k * (Input.Value - _lastEma) + _lastEma; + + // _useSma decides if we use compensator or not + result = (_useSma || _e == 0) ? _ema : _ema / (1 - _e); + } + _lastEma = _ema; + IsHot = _index >= WarmupPeriod; + return result; + } +} diff --git a/lib/averages/Epma.cs b/lib/averages/Epma.cs new file mode 100644 index 00000000..4d55e203 --- /dev/null +++ b/lib/averages/Epma.cs @@ -0,0 +1,82 @@ +namespace QuanTAlib; + +public class Epma : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Epma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Epma"; + WarmupPeriod = period; + Init(); + } + + public Epma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + + // Adjust for partial periods during warmup + if (_index < _period) + { + double[] partialKernel = GenerateKernel(_index); + result /= partialKernel.Sum(); + } + + IsHot = _index >= WarmupPeriod; + + return result; + } + + public static double[] GenerateKernel(int period) + { + double[] kernel = new double[period]; + double weightSum = 0; + + for (int i = 0; i < period; i++) + { + kernel[i] = (2 * period - 1) - 3 * i; + weightSum += kernel[i]; + } + + // Normalize the kernel + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } +} \ No newline at end of file diff --git a/lib/averages/Frama.cs b/lib/averages/Frama.cs new file mode 100644 index 00000000..8a260cf3 --- /dev/null +++ b/lib/averages/Frama.cs @@ -0,0 +1,99 @@ +using System; + +namespace QuanTAlib +{ + public class Frama : AbstractBase + { + private readonly int _period; + private readonly double _fc; + private CircularBuffer _buffer; + private double _lastFrama; + private double _prevLastFrama; + + public Frama(int period, double fc = 0.5) : base() + { + if (period < 2) + throw new ArgumentException("Period must be at least 2", nameof(period)); + + _period = period; + _fc = fc; + _buffer = new CircularBuffer(period); + WarmupPeriod = period; + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + _lastFrama = 0; + _prevLastFrama = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _prevLastFrama = _lastFrama; + _index++; + } + else + { + _lastFrama = _prevLastFrama; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + if (_buffer.Count < _period) + { + _lastFrama = _buffer.Average(); + return _lastFrama; + } + + int half = _period / 2; + double hh = double.MinValue, ll = double.MaxValue; + double hh1 = double.MinValue, ll1 = double.MaxValue; + double hh2 = double.MinValue, ll2 = double.MaxValue; + + for (int i = 0; i < _period; i++) + { + double price = _buffer[i]; + hh = Math.Max(hh, price); + ll = Math.Min(ll, price); + + if (i < half) + { + hh1 = Math.Max(hh1, price); + ll1 = Math.Min(ll1, price); + } + else + { + hh2 = Math.Max(hh2, price); + ll2 = Math.Min(ll2, price); + } + } + + double n1 = (hh - ll) / _period; + double n2 = (hh1 - ll1 + hh2 - ll2) / (_period / 2); + + double d = (Math.Log(n2 + double.Epsilon) - Math.Log(n1 + double.Epsilon)) / Math.Log(2); + + double alpha = Math.Exp(-4.6 * (d - 1)); + alpha = Math.Max(Math.Min(alpha, 1), 0.01); // Ensure alpha is between 0.01 and 1 + + _lastFrama = alpha * (Input.Value - _lastFrama) + _lastFrama; + + IsHot = _index >= WarmupPeriod; + return _lastFrama; + } + + protected override double GetLastValid() + { + return _lastFrama; + } + } +} \ No newline at end of file diff --git a/lib/averages/Fwma.cs b/lib/averages/Fwma.cs new file mode 100644 index 00000000..9746036a --- /dev/null +++ b/lib/averages/Fwma.cs @@ -0,0 +1,83 @@ +namespace QuanTAlib; + +public class Fwma : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Fwma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Fwma"; + WarmupPeriod = period; + Init(); + } + + public Fwma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public static double[] GenerateKernel(int period) + { + double[] kernel = new double[period]; + double[] fibSeries = new double[period]; + double weightSum = 0; + + // Generate Fibonacci series + fibSeries[0] = fibSeries[1] = 1; + for (int i = 2; i < period; i++) + { + fibSeries[i] = fibSeries[i - 1] + fibSeries[i - 2]; + } + + // Reverse the series to give more weight to recent prices + for (int i = 0; i < period; i++) + { + kernel[i] = fibSeries[period - 1 - i]; + weightSum += kernel[i]; + } + + // Normalize the kernel + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + IsHot = _index >= WarmupPeriod; + + return result; + } +} \ No newline at end of file diff --git a/lib/averages/Gma.cs b/lib/averages/Gma.cs new file mode 100644 index 00000000..4a0780fc --- /dev/null +++ b/lib/averages/Gma.cs @@ -0,0 +1,76 @@ +namespace QuanTAlib; + +public class Gma : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Gma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Gma"; + WarmupPeriod = period; + Init(); + } + + public Gma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public static double[] GenerateKernel(int period, double sigma = 1.0) + { + double[] kernel = new double[period]; + double weightSum = 0; + int center = period / 2; + + for (int i = 0; i < period; i++) + { + double x = (i - center) / (double)center; + kernel[i] = Math.Exp(-(x * x) / (2 * sigma * sigma)); + weightSum += kernel[i]; + } + + // Normalize the kernel + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + IsHot = _index >= WarmupPeriod; + + return result; + } +} \ No newline at end of file diff --git a/lib/averages/Hma.cs b/lib/averages/Hma.cs new file mode 100644 index 00000000..6486d689 --- /dev/null +++ b/lib/averages/Hma.cs @@ -0,0 +1,77 @@ +namespace QuanTAlib; + +public class Hma : AbstractBase +{ + private readonly int _period, _sqrtPeriod; + private readonly Convolution _wmaHalf, _wmaFull, _wmaFinal; + + public Hma(int period) + { + if (period < 2) + { + throw new ArgumentException("Period must be greater than or equal to 2.", nameof(period)); + } + _period = period; + _sqrtPeriod = (int)Math.Sqrt(period); + _wmaHalf = new Convolution(GenerateWmaKernel(period / 2)); + _wmaFull = new Convolution(GenerateWmaKernel(period)); + _wmaFinal = new Convolution(GenerateWmaKernel(_sqrtPeriod)); + Name = "Hma"; + WarmupPeriod = _period + _sqrtPeriod - 1; + Init(); + } + + public Hma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private static double[] GenerateWmaKernel(int period) + { + double[] kernel = new double[period]; + double weightSum = period * (period + 1) / 2.0; + + for (int i = 0; i < period; i++) + { + kernel[i] = (period - i) / weightSum; + } + + return kernel; + } + + private new void Init() + { + base.Init(); + _wmaHalf.Init(); + _wmaFull.Init(); + _wmaFinal.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Calculate WMA(n/2) and WMA(n) + double wmaHalfResult = _wmaHalf.Calc(Input).Value; + double wmaFullResult = _wmaFull.Calc(Input).Value; + + // Calculate 2*WMA(n/2) - WMA(n) + double intermediateResult = 2 * wmaHalfResult - wmaFullResult; + + // Calculate final WMA + double result = _wmaFinal.Calc(new TValue(Input.Time, intermediateResult, Input.IsNew)).Value; + + IsHot = _index >= WarmupPeriod; + return result; + } +} \ No newline at end of file diff --git a/lib/averages/Htit.cs b/lib/averages/Htit.cs new file mode 100644 index 00000000..08767763 --- /dev/null +++ b/lib/averages/Htit.cs @@ -0,0 +1,146 @@ +//not working yet +//TODO consistency test + +using QuanTAlib; + +public class Htit : AbstractBase +{ + private readonly CircularBuffer _priceBuffer = new(7); + private readonly CircularBuffer _spBuffer = new(7); + private readonly CircularBuffer _dtBuffer = new(7); + private readonly CircularBuffer _i1Buffer = new(7); + private readonly CircularBuffer _q1Buffer = new(7); + private readonly CircularBuffer _i2Buffer = new(2); + private readonly CircularBuffer _q2Buffer = new(2); + private readonly CircularBuffer _reBuffer = new(2); + private readonly CircularBuffer _imBuffer = new(2); + private readonly CircularBuffer _pdBuffer = new(2); + private readonly CircularBuffer _sdBuffer = new(2); + private readonly CircularBuffer _itBuffer = new(4); + + private double _lastPd = 0; + private double _p_lastPd = 0; + + public Htit() : base() + { + Name = "Htit"; + WarmupPeriod = 12; + } + + public Htit(object source) : this() + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_lastPd = _lastPd; + _index++; + } + else + { + _lastPd = _p_lastPd; + } + } + protected override double Calculation() + { + ManageState(Input.IsNew); + + double pr = Input.Value; + _priceBuffer.Add(pr, Input.IsNew); + + if (_index <= 5) + { + _spBuffer.Add(0, Input.IsNew); + _dtBuffer.Add(0, Input.IsNew); + _i1Buffer.Add(0, Input.IsNew); + _q1Buffer.Add(0, Input.IsNew); + _i2Buffer.Add(0, Input.IsNew); + _q2Buffer.Add(0, Input.IsNew); + _reBuffer.Add(0, Input.IsNew); + _imBuffer.Add(0, Input.IsNew); + _pdBuffer.Add(0, Input.IsNew); + _sdBuffer.Add(0, Input.IsNew); + _itBuffer.Add(pr, Input.IsNew); + return pr; + } + + double adj = (0.075 * _lastPd) + 0.54; + + // Smooth and detrender + double sp = ((4 * _priceBuffer[0]) + (3 * _priceBuffer[1]) + (2 * _priceBuffer[2]) + _priceBuffer[3]) / 10; + _spBuffer.Add(sp, Input.IsNew); + + double dt = ((0.0962 * _spBuffer[0]) + (0.5769 * _spBuffer[2]) - (0.5769 * _spBuffer[4]) - (0.0962 * _spBuffer[6])) * adj; + _dtBuffer.Add(dt, Input.IsNew); + + // In-phase and quadrature + double q1 = ((0.0962 * _dtBuffer[0]) + (0.5769 * _dtBuffer[2]) - (0.5769 * _dtBuffer[4]) - (0.0962 * _dtBuffer[6])) * adj; + _q1Buffer.Add(q1, Input.IsNew); + + double i1 = _dtBuffer[3]; + _i1Buffer.Add(i1, Input.IsNew); + + // Advance the phases by 90 degrees + double jI = ((0.0962 * _i1Buffer[0]) + (0.5769 * _i1Buffer[2]) - (0.5769 * _i1Buffer[4]) - (0.0962 * _i1Buffer[6])) * adj; + double jQ = ((0.0962 * _q1Buffer[0]) + (0.5769 * _q1Buffer[2]) - (0.5769 * _q1Buffer[4]) - (0.0962 * _q1Buffer[6])) * adj; + + // Phasor addition for 3-bar averaging + double i2 = i1 - jQ; + double q2 = q1 + jI; + + i2 = (0.2 * i2) + (0.8 * _i2Buffer[0]); + q2 = (0.2 * q2) + (0.8 * _q2Buffer[0]); + + _i2Buffer.Add(i2, Input.IsNew); + _q2Buffer.Add(q2, Input.IsNew); + + // Homodyne discriminator + double re = (i2 * _i2Buffer[1]) + (q2 * _q2Buffer[1]); + double im = (i2 * _q2Buffer[1]) - (q2 * _i2Buffer[1]); + + re = (0.2 * re) + (0.8 * _reBuffer[0]); + im = (0.2 * im) + (0.8 * _imBuffer[0]); + + _reBuffer.Add(re, Input.IsNew); + _imBuffer.Add(im, Input.IsNew); + + // Calculate period + double pd = (im != 0 && re != 0) ? 2 * Math.PI / Math.Atan(im / re) : 0; + + // Adjust period to thresholds + pd = (pd > 1.5 * _lastPd) ? 1.5 * _lastPd : pd; + pd = (pd < 0.67 * _lastPd) ? 0.67 * _lastPd : pd; + pd = (pd < 6) ? 6 : pd; + pd = (pd > 50) ? 50 : pd; + + // Smooth the period + pd = (0.2 * pd) + (0.8 * _lastPd); + _pdBuffer.Add(pd, Input.IsNew); + + double sd = (0.33 * pd) + (0.67 * _sdBuffer[0]); + _sdBuffer.Add(sd, Input.IsNew); + + // Smooth dominant cycle period + int dcPeriods = (int)(sd + 0.5); + double sumPr = _priceBuffer.GetSpan().Slice(0, Math.Min(dcPeriods, _priceBuffer.Count)).ToArray().Sum(); + double it = dcPeriods > 0 ? sumPr / dcPeriods : pr; + _itBuffer.Add(it, Input.IsNew); + + _p_lastPd = _lastPd; + _lastPd = pd; + + // Final indicator + if (_index >= 11) // 12th bar + { + return ((4 * _itBuffer[0]) + (3 * _itBuffer[1]) + (2 * _itBuffer[2]) + _itBuffer[3]) / 10; + } + else + { + return pr; + } + } +} \ No newline at end of file diff --git a/lib/averages/Hwma.cs b/lib/averages/Hwma.cs new file mode 100644 index 00000000..b6e8d492 --- /dev/null +++ b/lib/averages/Hwma.cs @@ -0,0 +1,96 @@ +namespace QuanTAlib; + +public class Hwma : AbstractBase +{ + private readonly int _period; + private readonly double _nA, _nB, _nC; + private double _pF, _pV, _pA; + private double _ppF, _ppV, _ppA; + + public Hwma(int period) : this(period, 2.0 / (1 + period), 1.0 / period, 1.0 / period) + { + } + + public Hwma(double nA, double nB, double nC) : this((int)((2 - nA) / nA), nA, nB, nC) + { + } + + public Hwma(int period, double nA, double nB, double nC) : base() + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _nA = nA; + _nB = nB; + _nC = nC; + WarmupPeriod = period; + Name = $"Hwma({_period})"; + Init(); + } + + public Hwma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _pF = _pV = _pA = 0; + _ppF = _ppV = _ppA = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _ppF = _pF; + _ppV = _pV; + _ppA = _pA; + } + else + { + _pF = _ppF; + _pV = _ppV; + _pA = _ppA; + + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + if (_index == 1) + { + _pF = Input.Value; + _pA = _pV = 0; + } + + double nA = _nA, nB = _nB, nC = _nC; + if (_period == 1) + { + nA = 1; + nB = 0; + nC = 0; + } + + double F = (1 - nA) * (_pF + _pV + 0.5 * _pA) + nA * Input.Value; + double V = (1 - nB) * (_pV + _pA) + nB * (F - _pF); + double A = (1 - nC) * _pA + nC * (V - _pV); + + double hwma = F + V + 0.5 * A; + + _pF = F; + _pV = V; + _pA = A; + + IsHot = _index >= WarmupPeriod; + return hwma; + } +} \ No newline at end of file diff --git a/lib/averages/Jma.cs b/lib/averages/Jma.cs new file mode 100644 index 00000000..e5a58b11 --- /dev/null +++ b/lib/averages/Jma.cs @@ -0,0 +1,130 @@ +using QuanTAlib; +//TODO consistency test +public class Jma : AbstractBase +{ + public readonly int Period; + private readonly double _phase; + private readonly int _vshort, _vlong; + private CircularBuffer _values; + private CircularBuffer _voltyShort; + private CircularBuffer _vsumBuff; + private CircularBuffer _avoltyBuff; + + private double _beta, _len1, _pow1; + private double _upperBand, _lowerBand, _prevMa1, _prevDet0, _prevDet1, _prevJma; + private double _p_UpperBand, _p_LowerBand, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma; + + public Jma(int period, double phase = 0, int vshort = 10) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + Period = period; + _vshort = vshort; + _vlong = 65; + _phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5); + + _values = new CircularBuffer(period); + _voltyShort = new CircularBuffer(vshort); + _vsumBuff = new CircularBuffer(_vlong); + _avoltyBuff = new CircularBuffer(2); + + Name = "JMA"; + WarmupPeriod = period * 2; + Init(); + } + + public override void Init() + { + _upperBand = _lowerBand = _prevMa1 = _prevDet0 = _prevDet1 = _prevJma = 0.0; + _p_UpperBand = _p_LowerBand = _p_prevMa1 = _p_prevDet0 = _p_prevDet1 = _p_prevJma = 0.0; + _beta = 0.45 * (Period - 1) / (0.45 * (Period - 1) + 2); + _len1 = Math.Max((Math.Log(Math.Sqrt(Period - 1)) / Math.Log(2.0)) + 2.0, 0); + _pow1 = Math.Max(_len1 - 2.0, 0.5); + _avoltyBuff.Clear(); + _avoltyBuff.Add(0, true); + _avoltyBuff.Add(0, true); + base.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + // Save current state + _p_UpperBand = _upperBand; + _p_LowerBand = _lowerBand; + _p_prevMa1 = _prevMa1; + _p_prevDet0 = _prevDet0; + _p_prevDet1 = _prevDet1; + _p_prevJma = _prevJma; + } + else + { + // Restore previous state + _upperBand = _p_UpperBand; + _lowerBand = _p_LowerBand; + _prevMa1 = _p_prevMa1; + _prevDet0 = _p_prevDet0; + _prevDet1 = _p_prevDet1; + _prevJma = _p_prevJma; + + } + } + protected override double Calculation() + { + ManageState(Input.IsNew); + + _values.Add(Input.Value, Input.IsNew); + + if (_index == 1) + { + _prevMa1 = _prevJma = Input.Value; + return Input.Value; + } + + double hprice = _values.Max(); + double lprice = _values.Min(); + + double del1 = hprice - _upperBand; + double del2 = lprice - _lowerBand; + double volty = Math.Max(Math.Abs(del1), Math.Abs(del2)); + + _voltyShort.Add(volty, Input.IsNew); + double vsum = _vsumBuff.Newest() + 0.1 * (volty - _voltyShort.Oldest()); + _vsumBuff.Add(vsum, Input.IsNew); + + double prevAvolty = _avoltyBuff.Newest(); + double avolty = prevAvolty + 2.0 / (Math.Max(4.0 * Period, 30) + 1.0) * (vsum - prevAvolty); + _avoltyBuff.Add(avolty, Input.IsNew); + + double dVolty = (avolty > 0) ? volty / avolty : 0; + dVolty = Math.Min(Math.Max(dVolty, 1.0), Math.Pow(_len1, 1.0 / _pow1)); + + double pow2 = Math.Pow(dVolty, _pow1); + double len2 = Math.Sqrt(0.5 * (Period - 1)) * _len1; + double _Kv = Math.Pow(len2 / (len2 + 1), Math.Sqrt(pow2)); + + _upperBand = (del1 > 0) ? hprice : hprice - (_Kv * del1); + _lowerBand = (del2 < 0) ? lprice : lprice - (_Kv * del2); + + double alpha = Math.Pow(_beta, pow2); + double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1; + _prevMa1 = ma1; + + double det0 = (1 - _beta) * (Input.Value - ma1) + _beta * _prevDet0; + _prevDet0 = det0; + double ma2 = ma1 + (_phase + 1) * det0; + + double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - _prevJma)) + (alpha * alpha * _prevDet1); + _prevDet1 = det1; + double jma = _prevJma + det1; + _prevJma = jma; + + IsHot = _index >= WarmupPeriod; + return jma; + } +} \ No newline at end of file diff --git a/lib/averages/Kama.cs b/lib/averages/Kama.cs new file mode 100644 index 00000000..81cda9cf --- /dev/null +++ b/lib/averages/Kama.cs @@ -0,0 +1,85 @@ +using System; + +namespace QuanTAlib; + +public class Kama : AbstractBase +{ + private readonly int _period; + private readonly double _scFast, _scSlow; + private CircularBuffer? _buffer; + private double _lastKama, _p_lastKama; + + public Kama(int period, int fast = 2, int slow = 30) : base() + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _scFast = 2.0 / (((period < fast) ? period : fast) + 1); + _scSlow = 2.0 / (slow + 1); + WarmupPeriod = period; + Name = $"Kama({_period}, {fast}, {slow})"; + Init(); + } + + public Kama(object source, int period, int fast = 2, int slow = 30) : this(period, fast, slow) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer = new CircularBuffer(_period + 1); + _lastKama = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _p_lastKama = _lastKama; + } + else + { + _lastKama = _p_lastKama; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer!.Add(Input.Value, Input.IsNew); + + double kama; + if (_index <= _period) + { + kama = Input.Value; + } + else + { + double change = Math.Abs(_buffer[^1] - _buffer[0]); + double volatility = 0; + for (int i = 1; i < _buffer.Count; i++) + { + volatility += Math.Abs(_buffer[i] - _buffer[i - 1]); + } + + double er = volatility != 0 ? change / volatility : 0; + double sc = (er * (_scFast - _scSlow)) + _scSlow; + sc *= sc; // Square the smoothing constant + + kama = _lastKama + (sc * (Input.Value - _lastKama)); + } + + _lastKama = kama; + IsHot = _index >= WarmupPeriod; + + return kama; + } +} \ No newline at end of file diff --git a/lib/averages/Ltma.cs b/lib/averages/Ltma.cs new file mode 100644 index 00000000..5d64d249 --- /dev/null +++ b/lib/averages/Ltma.cs @@ -0,0 +1,74 @@ +namespace QuanTAlib; + +// https://www.mesasoftware.com/papers/TimeWarp.pdf + +public class Ltma : AbstractBase +{ + private readonly double _gamma; + private double _prevL0, _prevL1, _prevL2, _prevL3; + private double _p_prevL0, _p_prevL1, _p_prevL2, _p_prevL3; + + public double Gamma => _gamma; + + public Ltma(double gamma = 0.1) : base() + { + if (gamma < 0 || gamma > 1) + throw new ArgumentOutOfRangeException(nameof(gamma), "Gamma must be between 0 and 1."); + _gamma = gamma; + Name = $"Laguerre({gamma:F2})"; + WarmupPeriod = 4; // Minimum number of samples needed + Init(); + } + + public Ltma(object source, double gamma = 0.1) : this(gamma) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _prevL0 = _prevL1 = _prevL2 = _prevL3 = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_prevL0 = _prevL0; + _p_prevL1 = _prevL1; + _p_prevL2 = _prevL2; + _p_prevL3 = _prevL3; + _index++; + } + else + { + _prevL0 = _p_prevL0; + _prevL1 = _p_prevL1; + _prevL2 = _p_prevL2; + _prevL3 = _p_prevL3; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Laguerre filter calculation + double _l0 = (1 - _gamma) * Input.Value + _gamma * _prevL0; + double _l1 = -_gamma * _l0 + _prevL0 + _gamma * _prevL1; + double _l2 = -_gamma * _l1 + _prevL1 + _gamma * _prevL2; + double _l3 = -_gamma * _l2 + _prevL2 + _gamma * _prevL3; + _prevL0 = _l0; + _prevL1 = _l1; + _prevL2 = _l2; + _prevL3 = _l3; + + double filteredValue = (_l0 + 2 * _l1 + 2 * _l2 + _l3) / 6; + + IsHot = _index >= WarmupPeriod; + + return filteredValue; + } +} diff --git a/lib/averages/Maaf.cs b/lib/averages/Maaf.cs new file mode 100644 index 00000000..8230d4e8 --- /dev/null +++ b/lib/averages/Maaf.cs @@ -0,0 +1,110 @@ +//TODO: consistency test + +namespace QuanTAlib; + +// https://efs.kb.esignal.com/hc/en-us/articles/6362791434395-2005-Mar-The-Secret-Behind-The-Filter-MedianAdaptiveFilter-efs + +public class Maaf : AbstractBase +{ + private readonly CircularBuffer _priceBuffer; + private readonly CircularBuffer _smoothBuffer; + private double _prevFilter, _prevValue2, _threshold; + private double _p_prevFilter, _p_prevValue2; + + private readonly int _period; + + public Maaf(int Period = 39, double Threshold = 0.002) : base() + { + _period = Period; + _threshold = Threshold; + _priceBuffer = new CircularBuffer(4); + _smoothBuffer = new CircularBuffer(Period); + Name = "MAAF"; + WarmupPeriod = Period; + Init(); + } + + public Maaf(object source, int Period = 39, double Threshold = 0.002) : this(Period, Threshold) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + _priceBuffer.Clear(); + _smoothBuffer.Clear(); + _prevFilter = 0; + _prevValue2 = 0; + base.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _p_prevFilter = _prevFilter; + _p_prevValue2 = _prevValue2; + } + else + { + _prevFilter = _p_prevFilter; + _prevValue2 = _p_prevValue2; + } + } + + protected override double Calculation() + { + ManageState(IsNew); + + _priceBuffer.Add(Input.Value, Input.IsNew); + + if (_priceBuffer.Count < 4) + { + return Input.Value; + } + + double smooth = (_priceBuffer[^1] + (2 * _priceBuffer[^2]) + (2 * _priceBuffer[^3]) + _priceBuffer[^4]) / 6; + _smoothBuffer.Add(smooth, Input.IsNew); + + if (_smoothBuffer.Count < _period) + { + return smooth; + } + + int length = _period; + double value3 = 0.2; + double value2 = _prevValue2; + + while (value3 > _threshold && length > 0) + { + double alpha = 2.0 / (length + 1); + + var sortedValues = _smoothBuffer.TakeLast(length).OrderBy(x => x).ToList(); + double value1 = sortedValues[length / 2]; + value2 = alpha * (smooth - _prevValue2) + _prevValue2; + + if (value1 != 0) + { + value3 = Math.Abs(value1 - value2) / value1; + } + + length -= 2; + } + + if (length < 3) length = 3; + + double finalAlpha = 2.0 / (length + 1); + double filter = finalAlpha * (smooth - _prevFilter) + _prevFilter; + + _p_prevFilter = _prevFilter; + _prevFilter = filter; + _p_prevValue2 = _prevValue2; + _prevValue2 = value2; + + IsHot = _index >= WarmupPeriod; + return filter; + } +} diff --git a/lib/averages/Mama.cs b/lib/averages/Mama.cs new file mode 100644 index 00000000..de303cb4 --- /dev/null +++ b/lib/averages/Mama.cs @@ -0,0 +1,160 @@ +using QuanTAlib; +using System; + +public class Mama : AbstractBase +{ + private readonly double _fastLimit, _slowLimit; + private CircularBuffer _pr, _sm, _dt, _i1, _q1, _i2, _q2, _re, _im, _pd, _ph; + private double _mama, _fama; + private double _prevMama, _prevFama, _sumPr; + private double _p_prevMama, _p_prevFama, _p_sumPr; + + public TValue Fama { get; private set; } + + public Mama(double fastLimit = 0.5, double slowLimit = 0.05) : base() + { + Fama = new TValue(); + Name = $"Mama({_fastLimit:F2}, {_slowLimit:F2})"; + _fastLimit = fastLimit; + _slowLimit = slowLimit; + _pr = new(7); + _sm = new(7); + _dt = new(7); + _q1 = new(7); + _i1 = new(7); + _i2 = new(2); + _q2 = new(2); + _re = new(2); + _im = new(2); + _pd = new(2); + _ph = new(2); + Init(); + } + + public Mama(object source, double fastLimit = 0.5, double slowLimit = 0.05) : this(fastLimit, slowLimit) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + Fama = new TValue(); + base.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_prevMama = _prevMama; + _p_prevFama = _prevFama; + _p_sumPr = _sumPr; + _lastValidValue = Input.Value; + _index++; + } + else + { + _prevMama = _p_prevMama; + _prevFama = _p_prevFama; + _sumPr = _p_sumPr; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _pr.Add(Input.Value, Input.IsNew); + + if (_index > 6) + { + double adj = (0.075 * _pd[^1]) + 0.54; + + // Smooth + _sm.Add(((4 * _pr[^1]) + (3 * _pr[^2]) + (2 * _pr[^3]) + _pr[^4]) / 10, Input.IsNew); + + // Detrender + _dt.Add(((0.0962 * _sm[^1]) + (0.5769 * _sm[^3]) - (0.5769 * _sm[^5]) - (0.0962 * _sm[^7])) * adj, Input.IsNew); + + // In-phase and quadrature + _q1.Add(((0.0962 * _dt[^1]) + (0.5769 * _dt[^3]) - (0.5769 * _dt[^5]) - (0.0962 * _dt[^7])) * adj, Input.IsNew); + _i1.Add(_dt[^4], Input.IsNew); + + // Advance the phases by 90 degrees + double jI = ((0.0962 * _i1[^1]) + (0.5769 * _i1[^3]) - (0.5769 * _i1[^5]) - (0.0962 * _i1[^7])) * adj; + double jQ = ((0.0962 * _q1[^1]) + (0.5769 * _q1[^3]) - (0.5769 * _q1[^5]) - (0.0962 * _q1[^7])) * adj; + + // Phasor addition for 3-bar averaging + _i2.Add(_i1[^1] - jQ, Input.IsNew); + _q2.Add(_q1[^1] + jI, Input.IsNew); + _i2[^1] = 0.2 * _i2[^1] + 0.8 * _i2[^2]; + _q2[^1] = 0.2 * _q2[^1] + 0.8 * _q2[^2]; + + // Homodyne discriminator + _re.Add((_i2[^1] * _i2[^2]) + (_q2[^1] * _q2[^2]), Input.IsNew); + _im.Add((_i2[^1] * _q2[^2]) - (_q2[^1] * _i2[^2]), Input.IsNew); + _re[^1] = (0.2 * _re[^1]) + (0.8 * _re[^2]); + _im[^1] = (0.2 * _im[^1]) + (0.8 * _im[^2]); + + // Calculate period + if (_im[^1] != 0 && _re[^1] != 0) + { + _pd.Add(2 * Math.PI / Math.Atan(_im[^1] / _re[^1]), Input.IsNew); + } + else + { + _pd.Add(_pd[^2], Input.IsNew); + } + + // Adjust period to thresholds + _pd[^1] = Math.Max(Math.Min(_pd[^1], 1.5 * _pd[^2]), 0.67 * _pd[^2]); + _pd[^1] = Math.Max(Math.Min(_pd[^1], 50), 6); + _pd[^1] = (0.2 * _pd[^1]) + (0.8 * _pd[^2]); + + // Determine phase position + if (_i1[^1] != 0) + { + _ph.Add(Math.Atan(_q1[^1] / _i1[^1]) * 180 / Math.PI, Input.IsNew); + } + else + { + _ph.Add(_ph[^2], Input.IsNew); + } + + // Change in phase + double delta = Math.Max(_ph[^2] - _ph[^1], 1); + + // Adaptive alpha value + double alpha = Math.Max(_fastLimit / delta, _slowLimit); + + // Final indicators + _mama = alpha * (_pr[^1] - _prevMama) + _prevMama; + _fama = 0.5 * alpha * (_mama - _prevFama) + _prevFama; + + _prevMama = _mama; + _prevFama = _fama; + } + else + { + _pd.Add(0, Input.IsNew); + _sm.Add(0, Input.IsNew); + _dt.Add(0, Input.IsNew); + _i1.Add(0, Input.IsNew); + _q1.Add(0, Input.IsNew); + _i2.Add(0, Input.IsNew); + _q2.Add(0, Input.IsNew); + _re.Add(0, Input.IsNew); + _im.Add(0, Input.IsNew); + _ph.Add(0, Input.IsNew); + + _sumPr += Input.Value; + _mama = _fama = _prevMama = _prevFama = _sumPr / _index; + } + + Fama = new TValue(Time: Input.Time, Value: _fama, IsNew: Input.IsNew); + IsHot = _index >= 6; + + return _mama; + } +} diff --git a/lib/averages/Mgdi.cs b/lib/averages/Mgdi.cs new file mode 100644 index 00000000..59970815 --- /dev/null +++ b/lib/averages/Mgdi.cs @@ -0,0 +1,66 @@ +namespace QuanTAlib; + +public class Mgdi : AbstractBase +{ + private readonly int _period; + private readonly double _kFactor; + private double _prevMd, _p_prevMd; + public Mgdi(int period, double kFactor = 0.6) : base() + { + if (period <= 0) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0."); + } + if (kFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(kFactor), "K-Factor must be greater than 0."); + } + _period = period; + _kFactor = kFactor; + Name = "Mgdi"; + WarmupPeriod = period; + Init(); + } + + public Mgdi(object source, int period, double kFactor = 1.0) : this(period, kFactor) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _prevMd = _p_prevMd = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_prevMd = _prevMd; + _index++; + } else { + _prevMd = _p_prevMd; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double value = Input.Value; + if (_index < 2){ + _prevMd = value; + } + else + { + double md = _prevMd + ((value - _prevMd) / + (_kFactor * _period * Math.Pow(value / _prevMd, 4))); + _prevMd = md; + } + + IsHot = _index >= _period; + return _prevMd; + } +} \ No newline at end of file diff --git a/lib/averages/Mma.cs b/lib/averages/Mma.cs new file mode 100644 index 00000000..0a7d27a6 --- /dev/null +++ b/lib/averages/Mma.cs @@ -0,0 +1,78 @@ +using System; +using System.Linq; + +namespace QuanTAlib +{ + public class Mma : AbstractBase + { + private readonly int _period; + private readonly CircularBuffer _buffer; + private double _lastMma; + + public Mma(int period) : base() + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + _period = period; + _buffer = new CircularBuffer(period); + Name = "Mma"; + WarmupPeriod = period; + Init(); + } + + public Mma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _lastMma = 0; + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + if (_index >= _period) + { + double T = _buffer.Sum(); + double S = CalculateWeightedSum(); + _lastMma = (T / _period) + (6 * S) / ((_period + 1) * _period); + } + else + { + // Use simple average until we have enough data points + _lastMma = _buffer.Average(); + } + + IsHot = _index >= _period; + return _lastMma; + } + + private double CalculateWeightedSum() + { + double sum = 0; + for (int i = 0; i < _period; i++) + { + double weight = (_period - (2 * i + 1)) / 2.0; + sum += weight * _buffer[^(i + 1)]; + } + return sum; + } + } +} \ No newline at end of file diff --git a/lib/averages/Qema.cs b/lib/averages/Qema.cs new file mode 100644 index 00000000..f032cd07 --- /dev/null +++ b/lib/averages/Qema.cs @@ -0,0 +1,74 @@ +namespace QuanTAlib; + +public class Qema : AbstractBase +{ + private readonly double _k1, _k2, _k3, _k4; + private readonly Ema _ema1, _ema2, _ema3, _ema4; + private double _lastQema, _p_lastQema; + + public Qema(double k1=0.2, double k2=0.2, double k3=0.2, double k4=0.2) : base() + { + if (k1 <= 0 || k2 <= 0 || k3 <= 0 || k4 <= 0 ) + { + throw new ArgumentOutOfRangeException("All k values must be in the range (0, 1]."); + } + + _k1 = k1; + _k2 = k2; + _k3 = k3; + _k4 = k4; + + _ema1 = new Ema(k1); + _ema2 = new Ema(k2); + _ema3 = new Ema(k3); + _ema4 = new Ema(k4); + + Name = $"QEMA ({k1:F2},{k2:F2},{k3:F2},{k4:F2})"; + double smK = Math.Min(Math.Min(_k1, _k2), Math.Min(_k3, _k4)); + + WarmupPeriod = (int) ((2 - smK) / smK); + Init(); + } + + public Qema(object source, double k1, double k2, double k3, double k4) + : this(k1, k2, k3, k4) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _lastQema = 0; + _p_lastQema = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_lastQema = _lastQema; + _index++; + } + else + { + _lastQema = _p_lastQema; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double ema1 = _ema1.Calc(new TValue(Input.Time, Input.Value, Input.IsNew)); + double ema2 = _ema2.Calc(new TValue(Input.Time, ema1, Input.IsNew)); + double ema3 = _ema3.Calc(new TValue(Input.Time, ema2, Input.IsNew)); + double ema4 = _ema4.Calc(new TValue(Input.Time, ema3, Input.IsNew)); + + _lastQema = 4 * ema1 - 6 * ema2 + 4 * ema3 - ema4; + + IsHot = _index >= WarmupPeriod; + return _lastQema; + } +} \ No newline at end of file diff --git a/lib/averages/Rema.cs b/lib/averages/Rema.cs new file mode 100644 index 00000000..c89782c7 --- /dev/null +++ b/lib/averages/Rema.cs @@ -0,0 +1,78 @@ +namespace QuanTAlib; + +//https://user42.tuxfamily.org/chart/manual/Regularized-Exponential-Moving-Average.html + +public class Rema : AbstractBase +{ + private readonly int _period; + private readonly double _lambda; + private double _lastRema, _prevRema; + private double _savedLastRema, _savedPrevRema; + + public int Period => _period; + public double Lambda => _lambda; + + public Rema(int period, double lambda = 0.5) : base() + { + if (period < 1) + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + if (lambda < 0) + throw new ArgumentOutOfRangeException(nameof(lambda), "Lambda must be non-negative."); + + _period = period; + _lambda = lambda; + Name = $"REMA({period},{lambda:F2})"; + WarmupPeriod = period; + Init(); + } + + public override void Init() + { + base.Init(); + _lastRema = 0; + _prevRema = 0; + _savedLastRema = 0; + _savedPrevRema = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _savedLastRema = _lastRema; + _savedPrevRema = _prevRema; + _index++; + } + else + { + _lastRema = _savedLastRema; + _prevRema = _savedPrevRema; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double alpha = 2.0 / (Math.Min(_period, _index) + 1); + + if (_index > 2) + { + double rema = (_lastRema + alpha * (Input.Value - _lastRema) + _lambda * (_lastRema + (_lastRema - _prevRema))) / (1 + _lambda); + _prevRema = _lastRema; + _lastRema = rema; + } + else if (_index == 2) + { + _prevRema = _lastRema; + _lastRema = Input.Value; + } + else + { // _index == 1 + _lastRema = Input.Value; + } + + IsHot = _index >= WarmupPeriod; + return _lastRema; + } +} \ No newline at end of file diff --git a/lib/averages/Rma.cs b/lib/averages/Rma.cs new file mode 100644 index 00000000..3dadbba2 --- /dev/null +++ b/lib/averages/Rma.cs @@ -0,0 +1,65 @@ +using System; +using System.Runtime.CompilerServices; + +namespace QuanTAlib { + +public class Rma : AbstractBase { + private readonly int _period; + private double _alpha; + private double _lastRMA; + private double _savedLastRMA; + + public Rma(int period) : base() { + if (period < 1) { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + WarmupPeriod = period * 2; + _alpha = 1.0 / _period; // Wilder's smoothing factor + Name = $"Rma({_period})"; + Init(); + } + + public Rma(object source, int period) : this(period) { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() { + base.Init(); + _lastRMA = 0; + _savedLastRMA = 0; + } + + protected override void ManageState(bool isNew) { + if (isNew) { + _savedLastRMA = _lastRMA; + _lastValidValue = Input.Value; + _index++; + } else { + _lastRMA = _savedLastRMA; + } + } + + protected override double Calculation() { + ManageState(Input.IsNew); + + double rma; + + if (_index == 1) { + rma = Input.Value; + } else if (_index <= _period) { + // Simple average during initial period + rma = (_lastRMA * (_index - 1) + Input.Value) / _index; + } else { + // Wilder's smoothing method + rma = _alpha * (Input.Value - _lastRMA) + _lastRMA; + } + + _lastRMA = rma; + IsHot = _index >= WarmupPeriod; + + return rma; + } +} +} \ No newline at end of file diff --git a/lib/averages/Sinema.cs b/lib/averages/Sinema.cs new file mode 100644 index 00000000..ddba6f94 --- /dev/null +++ b/lib/averages/Sinema.cs @@ -0,0 +1,75 @@ +namespace QuanTAlib; + +public class Sinema : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Sinema(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Sinema"; + WarmupPeriod = period; + Init(); + } + + public Sinema(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + IsHot = _index >= WarmupPeriod; + + return result; + } + + public static double[] GenerateKernel(int period) + { + double[] kernel = new double[period]; + double weightSum = 0; + + for (int i = 0; i < period; i++) + { + // Use sine function to generate weights + kernel[i] = Math.Sin((i + 1) * Math.PI / (period + 1)); + weightSum += kernel[i]; + } + + // Normalize the kernel + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } +} \ No newline at end of file diff --git a/lib/averages/Sma.cs b/lib/averages/Sma.cs new file mode 100644 index 00000000..5cedd225 --- /dev/null +++ b/lib/averages/Sma.cs @@ -0,0 +1,59 @@ +namespace QuanTAlib; + +public class Sma : AbstractBase +{ + // inherited _index + // inherited _value + public readonly int Period; + private CircularBuffer _buffer; + + public Sma(int period) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + Period = period; + WarmupPeriod = period; + _buffer = new CircularBuffer(period); + Name = "Sma"; + WarmupPeriod = period; + Init(); + } + + public Sma(object source, int period) : this(period: period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + //inhereted public void Sub(object source, in ValueEventArgs args) + + public override void Init() + { + base.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Core SMA calculation - using _buffer.Average + /// + + protected override double Calculation() + { + double result; + ManageState(IsNew); + _buffer.Add(Input.Value, Input.IsNew); + result = _buffer.Average(); + + IsHot = _index >= WarmupPeriod; + return result; + } +} \ No newline at end of file diff --git a/lib/averages/Smma.cs b/lib/averages/Smma.cs new file mode 100644 index 00000000..1e20301c --- /dev/null +++ b/lib/averages/Smma.cs @@ -0,0 +1,78 @@ +using System; + +namespace QuanTAlib; + +public class Smma : AbstractBase +{ + private readonly int _period; + private CircularBuffer? _buffer; + private double _lastSmma, _p_lastSmma; + + public Smma(int period) : base() + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + WarmupPeriod = period; + Name = $"Smma({_period})"; + Init(); + } + + public Smma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer = new CircularBuffer(_period); + _lastSmma = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _p_lastSmma = _lastSmma; + _index++; + } + else + { + _lastSmma = _p_lastSmma; + } + } + + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer!.Add(Input.Value, Input.IsNew); + + double smma; + + if (_index <= _period) + { + smma = _buffer.Average(); + + if (_index == _period) + { + _lastSmma = smma; // Initialize _lastSmma for the transition + } + } + else + { + smma = ((_lastSmma * (_period - 1)) + Input.Value) / _period; + } + + _lastSmma = smma; + IsHot = _index >= WarmupPeriod; + + return smma; + } +} \ No newline at end of file diff --git a/lib/averages/T3.cs b/lib/averages/T3.cs new file mode 100644 index 00000000..5e7a30df --- /dev/null +++ b/lib/averages/T3.cs @@ -0,0 +1,133 @@ +namespace QuanTAlib; + +public class T3 : AbstractBase +{ + private readonly int _period; + private readonly double _vfactor; + private readonly bool _useSma; + private readonly double _k, _k1m, _c1, _c2, _c3, _c4; + private readonly CircularBuffer _buffer1, _buffer2, _buffer3, _buffer4, _buffer5, _buffer6; + private double _lastEma1, _lastEma2, _lastEma3, _lastEma4, _lastEma5, _lastEma6; + private double _p_lastEma1, _p_lastEma2, _p_lastEma3, _p_lastEma4, _p_lastEma5, _p_lastEma6; + + public T3(int period, double vfactor = 0.7, bool useSma = true) : base() + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _vfactor = vfactor; + _useSma = useSma; + WarmupPeriod = period; + + _k = 2.0 / (_period + 1); + _k1m = 1.0 - _k; + _c1 = -_vfactor * _vfactor * _vfactor; + _c2 = 3 * _vfactor * _vfactor + 3 * _vfactor * _vfactor * _vfactor; + _c3 = -6 * _vfactor * _vfactor - 3 * _vfactor - 3 * _vfactor * _vfactor * _vfactor; + _c4 = 1 + 3 * _vfactor + _vfactor * _vfactor * _vfactor + 3 * _vfactor * _vfactor; + + _buffer1 = new(period); + _buffer2 = new(period); + _buffer3 = new(period); + _buffer4 = new(period); + _buffer5 = new(period); + _buffer6 = new(period); + + + Name = $"T3({_period}, {_vfactor})"; + Init(); + } + + public T3(object source, int period, double vfactor = 0.7, bool useSma = true) : this(period, vfactor, useSma) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _lastEma1 = _lastEma2 = _lastEma3 = _lastEma4 = _lastEma5 = _lastEma6 = 0; + _buffer1.Clear(); + _buffer2.Clear(); + _buffer3.Clear(); + _buffer4.Clear(); + _buffer5.Clear(); + _buffer6.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _p_lastEma1 = _lastEma1; + _p_lastEma2 = _lastEma2; + _p_lastEma3 = _lastEma3; + _p_lastEma4 = _lastEma4; + _p_lastEma5 = _lastEma5; + _p_lastEma6 = _lastEma6; + } + else + { + _lastEma1 = _p_lastEma1; + _lastEma2 = _p_lastEma2; + _lastEma3 = _p_lastEma3; + _lastEma4 = _p_lastEma4; + _lastEma5 = _p_lastEma5; + _lastEma6 = _p_lastEma6; + } + } + + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double ema1, ema2, ema3, ema4, ema5, ema6; + + if (_index == 1) + { + ema1 = ema2 = ema3 = ema4 = ema5 = ema6 = Input.Value; + } + else if (_index <= _period && _useSma) + { + _buffer1.Add(Input.Value, Input.IsNew); + ema1 = _buffer1.Average(); + _buffer2.Add(ema1, Input.IsNew); + ema2 = _buffer2.Average(); + _buffer3.Add(ema2, Input.IsNew); + ema3 = _buffer3.Average(); + _buffer4.Add(ema3, Input.IsNew); + ema4 = _buffer4.Average(); + _buffer5.Add(ema4, Input.IsNew); + ema5 = _buffer5.Average(); + _buffer6.Add(ema5, Input.IsNew); + ema6 = _buffer6.Average(); + } + else + { + ema1 = _k * (Input.Value - _lastEma1) + _lastEma1; + ema2 = _k * (ema1 - _lastEma2) + _lastEma2; + ema3 = _k * (ema2 - _lastEma3) + _lastEma3; + ema4 = _k * (ema3 - _lastEma4) + _lastEma4; + ema5 = _k * (ema4 - _lastEma5) + _lastEma5; + ema6 = _k * (ema5 - _lastEma6) + _lastEma6; + } + + _lastEma1 = ema1; + _lastEma2 = ema2; + _lastEma3 = ema3; + _lastEma4 = ema4; + _lastEma5 = ema5; + _lastEma6 = ema6; + + double t3 = _c1 * ema6 + _c2 * ema5 + _c3 * ema4 + _c4 * ema3; + + IsHot = _index >= WarmupPeriod; + return t3; + } +} \ No newline at end of file diff --git a/lib/averages/Tema.cs b/lib/averages/Tema.cs new file mode 100644 index 00000000..2b12f6ee --- /dev/null +++ b/lib/averages/Tema.cs @@ -0,0 +1,81 @@ +namespace QuanTAlib; + +public class Tema : AbstractBase +{ + private readonly int _period; + private double _lastEma1, _p_lastEma1; + private double _lastEma2, _p_lastEma2; + private double _lastEma3, _p_lastEma3; + private double _k, _e, _p_e; + + public Tema(int period) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _period = period; + Name = "Tema"; + double percentile = 0.85; //targeting 85th percentile of correctness of converging EMA + WarmupPeriod = (int)Math.Ceiling(-period * Math.Log(1 - percentile)); + Init(); + } + + public Tema(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _k = 2.0 / (_period + 1); + _e = 1.0; + _lastEma1 = _lastEma2 = _lastEma3 = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_lastEma1 = _lastEma1; + _p_lastEma2 = _lastEma2; + _p_lastEma3 = _lastEma3; + _p_e = _e; + _index++; + } + else + { + _lastEma1 = _p_lastEma1; + _lastEma2 = _p_lastEma2; + _lastEma3 = _p_lastEma3; + _e = _p_e; + } + } + + protected override double Calculation() + { + double result, _ema1, _ema2, _ema3; + ManageState(Input.IsNew); + + _e = (_e > 1e-10) ? (1 - _k) * _e : 0; + double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1; + + _ema1 = _k * (Input.Value - _lastEma1) + _lastEma1; + + _ema2 = _k * (_ema1 * _invE - _lastEma2) + _lastEma2; + + _ema3 = _k * (_ema2 * _invE - _lastEma3) + _lastEma3; + + double _tema = 3 * _ema1 * _invE - 3 * _ema2 * _invE + _ema3 * _invE; + + result = _tema; + _lastEma1 = _ema1; + _lastEma2 = _ema2; + _lastEma3 = _ema3; + + IsHot = _index >= WarmupPeriod; + return result; + } +} \ No newline at end of file diff --git a/lib/averages/Trima.cs b/lib/averages/Trima.cs new file mode 100644 index 00000000..3abea746 --- /dev/null +++ b/lib/averages/Trima.cs @@ -0,0 +1,82 @@ +namespace QuanTAlib; + +public class Trima : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Trima(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Trima"; + WarmupPeriod = period; + Init(); + } + + public Trima(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private static double[] GenerateKernel(int period) + { + double[] kernel = new double[period]; + int halfPeriod = (period + 1) / 2; + double weightSum = 0; + + for (int i = 0; i < period; i++) + { + if (i < halfPeriod) + { + kernel[i] = i + 1; + } + else + { + kernel[i] = period - i; + } + weightSum += kernel[i]; + } + + // Normalize the kernel + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + IsHot = _index >= WarmupPeriod; + + return result; + } +} \ No newline at end of file diff --git a/lib/averages/Vidya.cs b/lib/averages/Vidya.cs new file mode 100644 index 00000000..e421c16c --- /dev/null +++ b/lib/averages/Vidya.cs @@ -0,0 +1,92 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace QuanTAlib; + +public class Vidya : AbstractBase +{ + private readonly int _shortPeriod; + private readonly int _longPeriod; + private readonly double _alpha; + private double _lastVIDYA, _p_lastVIDYA; + private CircularBuffer? _shortBuffer; + private CircularBuffer? _longBuffer; + + public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2) : base() + { + if (shortPeriod < 1) + { + throw new ArgumentException("Short period must be greater than or equal to 1.", nameof(shortPeriod)); + } + _shortPeriod = shortPeriod; + _longPeriod = (longPeriod == 0) ? shortPeriod * 4 : longPeriod; + _alpha = alpha; + WarmupPeriod = _longPeriod; + Name = $"Vidya({_shortPeriod},{_longPeriod})"; + Init(); + } + + public Vidya(object source, int shortPeriod, int longPeriod = 0, double alpha = 0.2) + : this(shortPeriod, longPeriod, alpha) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _lastVIDYA = 0; + _shortBuffer = new CircularBuffer(_shortPeriod); + _longBuffer = new CircularBuffer(_longPeriod); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _p_lastVIDYA = _lastVIDYA; + } + else + { + _lastVIDYA = _p_lastVIDYA; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _shortBuffer!.Add(Input.Value, Input.IsNew); + _longBuffer!.Add(Input.Value, Input.IsNew); + + double vidya; + if (_index <= _longPeriod) + { + vidya = _shortBuffer.Average(); + } + else + { + double shortStdDev = CalculateStdDev(_shortBuffer); + double longStdDev = CalculateStdDev(_longBuffer); + double s = _alpha * (shortStdDev / longStdDev); + vidya = (s * Input.Value) + ((1 - s) * _lastVIDYA); + } + + _lastVIDYA = vidya; + IsHot = _index >= WarmupPeriod; + + return vidya; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double CalculateStdDev(CircularBuffer buffer) + { + double mean = buffer.Average(); + double sumSquaredDiff = buffer.Sum(x => Math.Pow(x - mean, 2)); + return Math.Sqrt(sumSquaredDiff / buffer.Count); + } +} \ No newline at end of file diff --git a/lib/averages/Wma.cs b/lib/averages/Wma.cs new file mode 100644 index 00000000..9497ba25 --- /dev/null +++ b/lib/averages/Wma.cs @@ -0,0 +1,67 @@ +namespace QuanTAlib; + +public class Wma : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Wma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateWmaKernel(_period)); + Name = "Wma"; + WarmupPeriod = _period; + Init(); + } + + public Wma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private static double[] GenerateWmaKernel(int period) + { + double[] kernel = new double[period]; + double weightSum = period * (period + 1) / 2.0; + + for (int i = 0; i < period; i++) + { + kernel[i] = (period - i) / weightSum; + } + + return kernel; + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + IsHot = _index >= WarmupPeriod; + + return result; + } +} \ No newline at end of file diff --git a/lib/averages/Zlema.cs b/lib/averages/Zlema.cs new file mode 100644 index 00000000..b7115d66 --- /dev/null +++ b/lib/averages/Zlema.cs @@ -0,0 +1,73 @@ +using System; +using System.Runtime.CompilerServices; + +namespace QuanTAlib; + +public class Zlema : AbstractBase +{ + private readonly int _period; + private CircularBuffer? _buffer; + private double _alpha; + private int _lag; + private double _lastZLEMA, _p_lastZLEMA; + + public Zlema(int period) : base() + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + WarmupPeriod = period; + _alpha = 2.0 / (_period + 1); + _lag = (_period - 1) / 2; + Name = $"Zlema({_period})"; + Init(); + } + + public Zlema(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer = new CircularBuffer(_period); + _lastZLEMA = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _p_lastZLEMA = _lastZLEMA; + } + else + { + _lastZLEMA = _p_lastZLEMA; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer!.Add(Input.Value, Input.IsNew); + + int lag = Math.Max(Math.Min((int)((_period - 1) * 0.5), _buffer.Count - 1), 0) + 1; + double zlValue = 2 * Input.Value - _buffer[_buffer.Count - lag]; + + // Dynamic alpha factor for index <= period + double k = (_index <= _period) ? (2.0 / (_index + 1)) : _alpha; + double zlema = (zlValue - _lastZLEMA) * k + _lastZLEMA; + + _lastZLEMA = zlema; + IsHot = _index >= WarmupPeriod; + + return zlema; + } +} \ No newline at end of file diff --git a/lib/core/abstractBase.cs b/lib/core/abstractBase.cs new file mode 100644 index 00000000..84dfc144 --- /dev/null +++ b/lib/core/abstractBase.cs @@ -0,0 +1,82 @@ +namespace QuanTAlib; + +/// +/// Provides a base implementation for financial indicators in the QuanTAlib library. +/// This abstract class implements the iTValue interface and defines common properties +/// and methods used by inheriting indicator types. +/// +public abstract class AbstractBase : iTValue +{ + public DateTime Time { get; set; } + public double Value { get; set; } + public bool IsNew { get; set; } + public bool IsHot { get; set; } + + public TValue Input { get; set; } + public String Name { get; set; } = ""; + public int WarmupPeriod { get; set; } + + public TValue Tick => new(Time, Value, IsNew, IsHot); // Stores the current value of indicator + public event ValueSignal Pub = delegate { }; // Publisher of generated values + + protected int _index; //tracking the position of output + protected double _lastValidValue; + // other _internal vars defined here + + protected AbstractBase() + { //add parameters into constructor + } + + /// + /// Subscribes to a data source and triggers calculations on new data. + /// + /// The class publishing the data. + /// The argument containing the new data point. + public void Sub(object source, in ValueEventArgs args) => Calc(args.Tick); + + public virtual void Init() + { + _index = 0; + _lastValidValue = 0; + } + + /// + /// Calculates the indicator value based on the input; calls specific Calculation() method + /// where implementation is + /// + /// The input value for the calculation. + /// A TValue representing the calculated indicator value. + public virtual TValue Calc(TValue input) + { + Input = input; + if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) + { + return Process(new TValue(input.Time, GetLastValid(), input.IsNew, input.IsHot)); + } + this.Value = Calculation(); + return Process(new TValue(Time: Input.Time, Value: this.Value, IsNew: Input.IsNew, IsHot: this.IsHot)); + } + + protected virtual double GetLastValid() + { + return this.Value; + } + protected abstract void ManageState(bool isNew); + protected abstract double Calculation(); + + /// + /// Processes the calculated value, updates the indicator's own state, + /// and publishes the result through an event. + /// + /// The calculated TValue to process. + /// The processed TValue. + protected virtual TValue Process(TValue value) + { + this.Time = value.Time; + this.Value = value.Value; + this.IsNew = value.IsNew; + this.IsHot = value.IsHot; + Pub?.Invoke(this, new ValueEventArgs(value)); + return value; + } +} diff --git a/lib/core/circularbuffer.cs b/lib/core/circularbuffer.cs new file mode 100644 index 00000000..695c4da3 --- /dev/null +++ b/lib/core/circularbuffer.cs @@ -0,0 +1,332 @@ +using System.Collections; +using System.Runtime.CompilerServices; +using System.Numerics; + +namespace QuanTAlib; + +public class CircularBuffer : IEnumerable +{ + private readonly double[] _buffer; + private int _start = 0; + private int _size = 0; + + public int Capacity { get; } + public int Count => _size; + + public CircularBuffer(int capacity) + { + Capacity = capacity; + _buffer = GC.AllocateArray(capacity, pinned: true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(double item, bool isNew = true) + { + if (_size == 0 || isNew) + { + if (_size < Capacity) + { + _buffer[(_start + _size) % Capacity] = item; + _size++; + } + else + { + _buffer[_start] = item; + _start = (_start + 1) % Capacity; + } + } + else + { + _buffer[(_start + _size - 1) % Capacity] = item; + } + } + + public double this[Index index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int actualIndex = index.IsFromEnd ? _size - index.Value : index.Value; + actualIndex = Math.Clamp(actualIndex, 0, _size - 1); + return _buffer[(_start + actualIndex) % Capacity]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + int actualIndex = index.IsFromEnd ? _size - index.Value : index.Value; + actualIndex = Math.Clamp(actualIndex, 0, _size - 1); + _buffer[(_start + actualIndex) % Capacity] = value; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowArgumentOutOfRangeException() + { + throw new ArgumentOutOfRangeException("index", "Index is out of range."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Newest() + { + if (_size == 0) + return 0; + return _buffer[(_start + _size - 1) % Capacity]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Oldest() + { + if (_size == 0) + ThrowInvalidOperationException(); + return _buffer[_start]; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInvalidOperationException() + { + throw new InvalidOperationException("Buffer is empty."); + } + + public Enumerator GetEnumerator() => new(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator : IEnumerator + { + private readonly CircularBuffer _buffer; + private int _index; + private double _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(CircularBuffer buffer) + { + _buffer = buffer; + _index = -1; + _current = default; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (_index + 1 >= _buffer._size) + return false; + + _index++; + _current = _buffer[_index]; + return true; + } + + public double Current => _current; + object IEnumerator.Current => Current; + + public void Reset() + { + _index = -1; + _current = default; + } + + public void Dispose() { } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(double[] destination, int destinationIndex) + { + if (_size == 0) + return; + + if (_start + _size <= Capacity) + { + Array.Copy(_buffer, _start, destination, destinationIndex, _size); + } + else + { + int firstPartLength = Capacity - _start; + Array.Copy(_buffer, _start, destination, destinationIndex, firstPartLength); + Array.Copy(_buffer, 0, destination, destinationIndex + firstPartLength, _size - firstPartLength); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan GetSpan() + { + if (_size == 0) + return ReadOnlySpan.Empty; + + if (_start + _size <= Capacity) + { + return new ReadOnlySpan(_buffer, _start, _size); + } + else + { + return new ReadOnlySpan(ToArray()); + } + } + + public double[] InternalBuffer => _buffer; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan GetInternalSpan() => _buffer.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + Array.Clear(_buffer, 0, _buffer.Length); + _start = 0; + _size = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Max() + { + if (_size == 0) + ThrowInvalidOperationException(); + + return MaxSimd(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Min() + { + if (_size == 0) + ThrowInvalidOperationException(); + + return MinSimd(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Sum() + { + return SumSimd(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Average() + { + if (_size == 0) + ThrowInvalidOperationException(); + + return SumSimd() / _size; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double MaxSimd() + { + var span = GetSpan(); + var vectorSize = Vector.Count; + var maxVector = new Vector(double.MinValue); + + int i = 0; + for (; i <= span.Length - vectorSize; i += vectorSize) + { + maxVector = Vector.Max(maxVector, new Vector(span.Slice(i, vectorSize))); + } + + double max = double.MinValue; + for (int j = 0; j < vectorSize; j++) + { + max = Math.Max(max, maxVector[j]); + } + + for (; i < span.Length; i++) + { + max = Math.Max(max, span[i]); + } + + return max; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double MinSimd() + { + var span = GetSpan(); + var vectorSize = Vector.Count; + var minVector = new Vector(double.MaxValue); + + int i = 0; + for (; i <= span.Length - vectorSize; i += vectorSize) + { + minVector = Vector.Min(minVector, new Vector(span.Slice(i, vectorSize))); + } + + double min = double.MaxValue; + for (int j = 0; j < vectorSize; j++) + { + min = Math.Min(min, minVector[j]); + } + + for (; i < span.Length; i++) + { + min = Math.Min(min, span[i]); + } + + return min; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double SumSimd() + { + var span = GetSpan(); + var vectorSize = Vector.Count; + var sumVector = Vector.Zero; + + int i = 0; + for (; i <= span.Length - vectorSize; i += vectorSize) + { + sumVector += new Vector(span.Slice(i, vectorSize)); + } + + double sum = 0; + for (int j = 0; j < vectorSize; j++) + { + sum += sumVector[j]; + } + + for (; i < span.Length; i++) + { + sum += span[i]; + } + + return sum; + } + + public double[] ToArray() + { + double[] array = new double[_size]; + CopyTo(array, 0); + return array; + } + + public void ParallelOperation(Func operation) + { + const int MinimumPartitionSize = 1024; + + if (_size < MinimumPartitionSize) + { + var span = GetSpan(); + var array = span.ToArray(); + operation(array, 0, array.Length); + return; + } + + int partitionCount = Environment.ProcessorCount; + int partitionSize = _size / partitionCount; + + if (partitionSize < MinimumPartitionSize) + { + partitionCount = Math.Max(1, _size / MinimumPartitionSize); + partitionSize = _size / partitionCount; + } + + var buffer = ToArray(); + var results = new double[partitionCount]; + + Parallel.For(0, partitionCount, i => + { + int start = i * partitionSize; + int length = (i == partitionCount - 1) ? _size - start : partitionSize; + results[i] = operation(buffer, start, length); + }); + + } + +} \ No newline at end of file diff --git a/lib/core/formatters.cs b/lib/core/formatters.cs new file mode 100644 index 00000000..685ad8e2 --- /dev/null +++ b/lib/core/formatters.cs @@ -0,0 +1,97 @@ +using Microsoft.DotNet.Interactive.Formatting; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace QuanTAlib; +public static class Formatters +{ + const string smallfont = "smaller"; + const string pad = "18"; + public static void Initialize() + { + Formatter.Register((tick, writer) => + { + var sb = new StringBuilder(); + sb.Append(""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append("
{tick.Time:yyyy-MM-dd HH:mm:ss}{tick.Value:F2}{(tick.IsHot ? "🔥" : "❄️")}
"); + writer.Write(sb.ToString()); + }, HtmlFormatter.MimeType); + + Formatter.Register((series, writer) => + { + var sb = new StringBuilder(); + sb.Append(""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append(""); + + for (int i = 0; i < Math.Min(100, series.Count); i++) + { + TValue item = series[i]; + sb.Append(""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append(""); + } + sb.Append("
{series.Name}IndexValue
{item.Time:yyyy-MM-dd HH:mm:ss}{i}{item.Value:F2}{(item.IsHot ? "🔥" : "❄️")}
"); + if (series.Count > 100) + { + sb.Append("

Showing first 100 items. Total items: " + series.Count + "

"); + } + writer.Write(sb.ToString()); + }, HtmlFormatter.MimeType); + + Formatter.Register((bar, writer) => + { + var sb = new StringBuilder(); + sb.Append(""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append("
{bar.Time:yyyy-MM-dd HH:mm:ss}{bar.Open:F2}{bar.High:F2}{bar.Low:F2}{bar.Close:F2} {bar.Volume:F2}
"); + writer.Write(sb.ToString()); + }, HtmlFormatter.MimeType); + + Formatter.Register((series, writer) => + { + var sb = new StringBuilder(); + sb.Append(""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append(""); + for (int i = 0; i < Math.Min(100, series.Count); i++) + { + TBar item = series[i]; + sb.Append(""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append($""); + sb.Append(""); + } + sb.Append("
{series.Name}IndexOpenHighLowCloseVolume
{item.Time:yyyy-MM-dd HH:mm:ss}{i}{item.Open:F2}{item.High:F2}{item.Low:F2}{item.Close:F2}{item.Volume:F2}
"); + if (series.Count > 100) + { + sb.Append("

Showing first 100 items. Total items: " + series.Count + "

"); + } + writer.Write(sb.ToString()); + }, HtmlFormatter.MimeType); + } +} diff --git a/lib/core/tbar.cs b/lib/core/tbar.cs new file mode 100644 index 00000000..9f71e5e4 --- /dev/null +++ b/lib/core/tbar.cs @@ -0,0 +1,128 @@ +namespace QuanTAlib; + +public interface iTBar +{ + DateTime Time { get; } + double Open { get; } + double High { get; } + double Low { get; } + double Close { get; } + double Volume { get; } + bool IsNew { get; } +} + +public readonly record struct TBar(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : iTBar +{ + public DateTime Time { get; init; } = Time; + public double Open { get; init; } = Open; + public double High { get; init; } = High; + public double Low { get; init; } = Low; + public double Close { get; init; } = Close; + public double Volume { get; init; } = Volume; + public bool IsNew { get; init; } = IsNew; + + public double HL2 => (High + Low) * 0.5; + public double OC2 => (Open + Close) * 0.5; + public double OHL3 => (Open + High + Low) / 3; + public double HLC3 => (High + Low + Close) / 3; + public double OHLC4 => (Open + High + Low + Close) * 0.25; + public double HLCC4 => (High + Low + Close + Close) * 0.25; + + public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { } + public TBar(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : this(DateTime.UtcNow, Open, High, Low, Close, Volume, IsNew) { } + + // when TBar casts to double, it returns its Close + public static implicit operator double(TBar bar) => bar.Close; + public static implicit operator DateTime(TBar tv) => tv.Time; + + // castings for sloppy people - a single double injected into a TBar, and a single TValue injected into a TBar + public TBar(double value) : this(Time: DateTime.UtcNow, Open: value, High: value, Low: value, Close: value, Volume: value, IsNew: true) { } + public TBar(TValue value) : this(Time: value.Time, Open: value.Value, High: value.Value, Low: value.Value, Close: value.Value, Volume: value.Value, IsNew: value.IsNew) { } + + public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]"; +} + +public delegate void BarSignal(object source, in TBarEventArgs args); + +public class TBarEventArgs : EventArgs +{ + public TBar Bar { get; } + public TBarEventArgs(TBar bar) { Bar = bar; } +} + +public class TBarSeries : List +{ + private readonly TBar Default = new(DateTime.MinValue, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN); + + public TSeries Open; + public TSeries High; + public TSeries Low; + public TSeries Close; + public TSeries Volume; + + + public TBar Last => Count > 0 ? this[^1] : Default; + public TBar First => Count > 0 ? this[0] : Default; + public int Length => Count; + public string Name { get; set; } + public event BarSignal Pub = delegate { }; + + public TBarSeries() + { + this.Name = "Bar"; + Open = new(); + High = new(); + Low = new(); + Close = new(); + Volume = new(); + } + public TBarSeries(object source) : this() + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new BarSignal(Sub)); + } + + public new virtual void Add(TBar bar) + { + if (bar.IsNew) { base.Add(bar); } else { this[^1] = bar; } + Pub?.Invoke(this, new TBarEventArgs(bar)); + + Open.Add(bar.Time, bar.Open, IsNew: bar.IsNew, IsHot: true); + High.Add(bar.Time, bar.High, IsNew: bar.IsNew, IsHot: true); + Low.Add(bar.Time, bar.Low, IsNew: bar.IsNew, IsHot: true); + Close.Add(bar.Time, bar.Close, IsNew: bar.IsNew, IsHot: true); + Volume.Add(bar.Time, bar.Volume, IsNew: bar.IsNew, IsHot: true); + } + public void Add(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) => + this.Add(new TBar(Time, Open, High, Low, Close, Volume, IsNew)); + + public void Add(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) => + this.Add(new TBar(DateTime.Now, Open, High, Low, Close, Volume, IsNew)); + + public void Add(TBarSeries series) + { + if (series == this) + { + // If adding itself, create a copy to avoid modification during enumeration + var copy = new TBarSeries { Name = this.Name }; + copy.AddRange(this); + AddRange(copy); + } + else + { + AddRange(series); + } + } + public new virtual void AddRange(IEnumerable collection) + { + foreach (var item in collection) + { + Add(item); + } + } + + public void Sub(object source, in TBarEventArgs args) + { + Add(args.Bar); + } +} \ No newline at end of file diff --git a/lib/core/tvalue.cs b/lib/core/tvalue.cs new file mode 100644 index 00000000..30a67d29 --- /dev/null +++ b/lib/core/tvalue.cs @@ -0,0 +1,110 @@ +namespace QuanTAlib; + +public interface iTValue +{ + DateTime Time { get; } + double Value { get; } + bool IsNew { get; } + bool IsHot { get; } +} + +public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) : iTValue +{ + public DateTime Time { get; init; } = Time; + public double Value { get; init; } = Value; + public bool IsNew { get; init; } = IsNew; + public bool IsHot { get; init; } = IsHot; + public DateTime t => Time; + public double v => Value; + + public TValue() : this(DateTime.UtcNow, 0) { } + public TValue(double value, bool isNew = true, bool isHot = true) : this(DateTime.UtcNow, value, IsNew: isNew, IsHot: isHot) { } + public static implicit operator double(TValue tv) => tv.Value; + public static implicit operator DateTime(TValue tv) => tv.Time; + public static implicit operator TValue(double value) => new TValue(DateTime.UtcNow, value); + + public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}, {Value:F2}, IsNew: {IsNew}, IsHot: {IsHot}]"; +} + +public delegate void ValueSignal(object source, in ValueEventArgs args); + +public class ValueEventArgs : EventArgs +{ + public TValue Tick { get; } + public ValueEventArgs(TValue value) { Tick = value; } +} + +public class TSeries : List +{ + private readonly TValue Default = new(DateTime.MinValue, double.NaN); + public IEnumerable t => this.Select(item => item.t); + public IEnumerable v => this.Select(item => item.v); + public TValue Last => Count > 0 ? this[^1] : Default; + public TValue First => Count > 0 ? this[0] : Default; + public int Length => Count; + public string Name { get; set; } + public event ValueSignal Pub = delegate { }; + + public TSeries() { this.Name = "Data"; } + + public TSeries(object source) : this() + { + var pubEvent = source.GetType().GetEvent("Pub"); + if (pubEvent != null) + { + /* + var nameProperty = source.GetType().GetProperty("Name"); + if (nameProperty != null) { + Name = nameProperty.GetValue(nameProperty)?.ToString()!; + } + */ + pubEvent.AddEventHandler(source, new ValueSignal(Sub)); + } + } + public static explicit operator List(TSeries series) => series.Select(item => item.Value).ToList(); + public static explicit operator double[](TSeries series) => series.Select(item => item.Value).ToArray(); + + public new virtual void Add(TValue tick) + { + if (tick.IsNew) { base.Add(tick); } + else { this[^1] = tick; } + Pub?.Invoke(this, new ValueEventArgs(tick)); + } + public virtual void Add(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) => this.Add(new TValue(Time, Value, IsNew, IsHot)); + public virtual void Add(double Value, bool IsNew = true, bool IsHot = true) => this.Add(new TValue(DateTime.UtcNow, Value, IsNew, IsHot)); + + public void Add(IEnumerable values) + { + var valueList = values.ToList(); + int count = valueList.Count; + DateTime startTime = DateTime.UtcNow - TimeSpan.FromHours(count); + + for (int i = 0; i < count; i++) + { + this.Add(startTime, valueList[i]); + startTime = startTime.AddHours(1); + } + } + public void Add(TSeries series) + { + if (series == this) + { + // If adding itself, create a copy to avoid modification during enumeration + var copy = new TSeries { Name = this.Name }; + copy.AddRange(this); + AddRange(copy); + } + else + { + AddRange(series); + } + } + public new virtual void AddRange(IEnumerable collection) + { + foreach (var item in collection) + { + Add(item); + } + } + public void Sub(object source, in ValueEventArgs args) { Add(args.Tick); } +} \ No newline at end of file diff --git a/lib/feeds/GbmFeed.cs b/lib/feeds/GbmFeed.cs new file mode 100644 index 00000000..dfcf2365 --- /dev/null +++ b/lib/feeds/GbmFeed.cs @@ -0,0 +1,70 @@ +using System.CommandLine.Rendering.Views; + +namespace QuanTAlib; + +public class GbmFeed : TBarSeries +{ + private readonly double _mu, _sigma; + private readonly Random _random; + private double _lastClose, _lastHigh, _lastLow; + + public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2) : base() + { + _lastClose = _lastHigh = _lastLow = initialPrice; + _mu = mu; + _sigma = sigma; + _random = new Random((int)DateTime.Now.Ticks); + this.Name = $"GBM({_sigma:F2})"; + } + + public void Add(bool isNew = true) => Add(time: DateTime.Now, isNew: isNew); + public void Add(DateTime time, bool isNew = true) => base.Add(Generate(time, isNew)); + public void Add(int count) + { + DateTime startTime = DateTime.UtcNow - TimeSpan.FromHours(count); + TBar lastBar = new(); + for (int i = 0; i < count; i++) + { + Add(startTime, true); + Add(startTime, false); + Add(startTime, false); + startTime = startTime.AddHours(1); + } + } + + public TBar Generate(DateTime time, bool isNew = true) + { + double dt = 1.0 / 252; + double drift = (_mu - 0.5 * _sigma * _sigma) * dt; + double diffusion = _sigma * Math.Sqrt(dt) * GenerateNormalRandom(); + double newClose = _lastClose * Math.Exp(drift + diffusion); + + double open = _lastClose; + double high = Math.Max(_lastHigh, Math.Max(open, newClose) * (1 + _random.NextDouble() * 0.01)); + double low = Math.Min(_lastLow, Math.Min(open, newClose) * (1 - _random.NextDouble() * 0.01)); + double volume = 1000 + _random.NextDouble() * 1000; + + if (isNew) + { + _lastClose = newClose; + } + else + { + high = Math.Max(_lastHigh, high); + low = Math.Min(_lastLow, low); + } + _lastHigh = high; + _lastLow = low; + + TBar bar = new(time, open, high, low, newClose, volume, isNew); + return bar; + } + + private double GenerateNormalRandom() + { + // Box-Muller transform to generate standard normal random variable + double u1 = 1.0 - _random.NextDouble(); // Uniform(0,1] random doubles + double u2 = 1.0 - _random.NextDouble(); + return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); + } +} \ No newline at end of file diff --git a/lib/quantalib.csproj b/lib/quantalib.csproj new file mode 100644 index 00000000..4c228dec --- /dev/null +++ b/lib/quantalib.csproj @@ -0,0 +1,57 @@ + + + + QuanTAlib + Library of TA Calculations, Charts and Strategies for Quantower + Quantitative Technical Analysis Library in C# for Quantower + git + https://github.com/mihakralj/QuanTAlib + true + Miha Kralj + Miha Kralj + Apache-2.0 + readme.md + net8.0 + enable + preview + enable + false + en-US + QuanTAlib + QuanTAlib + True + AnyCPU + False + full + True + True + + Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo; + AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex; + Quantitative;Historical;Quotes; + + $(NoWarn);NU5104 + false + false + false + + + QuanTAlib2.png + https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png + True + + + + + + + + + ..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + + + + \ No newline at end of file diff --git a/lib/readme.md b/lib/readme.md new file mode 100644 index 00000000..58c4eba4 --- /dev/null +++ b/lib/readme.md @@ -0,0 +1 @@ +**Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis. \ No newline at end of file diff --git a/lib/statistics/Entropy.cs b/lib/statistics/Entropy.cs new file mode 100644 index 00000000..67b86482 --- /dev/null +++ b/lib/statistics/Entropy.cs @@ -0,0 +1,80 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +// Shannon's Entropy calculation +public class Entropy : AbstractBase +{ + public readonly int Period; + private CircularBuffer _buffer; + + public Entropy(int period) : base() + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for entropy calculation."); + } + Period = period; + WarmupPeriod = 2; + _buffer = new CircularBuffer(period); + Name = $"Entropy(period={period})"; + Init(); + } + + public Entropy(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double entropy = 0; + if (_index > 1) // We need at least two data points for entropy calculation + { + var values = _buffer.GetSpan().ToArray(); + int n = values.Length; + + // Calculate probabilities + var groupedValues = values.GroupBy(x => x).Select(g => new { Value = g.Key, Count = g.Count() }); + + // Use the actual count of values for probability calculation + foreach (var group in groupedValues) + { + double probability = (double)group.Count / n; + entropy -= probability * Math.Log2(probability); + } + + // Normalize the entropy based on the current number of unique values + int uniqueValueCount = groupedValues.Count(); + double maxEntropy = Math.Log2(uniqueValueCount); + + entropy = entropy == 0 ? 1 : entropy / maxEntropy; + + } + else { entropy = 1; } + + IsHot = _buffer.Count >= Period; + return entropy; + } +} \ No newline at end of file diff --git a/lib/statistics/Kurtosis.cs b/lib/statistics/Kurtosis.cs new file mode 100644 index 00000000..1a5be948 --- /dev/null +++ b/lib/statistics/Kurtosis.cs @@ -0,0 +1,76 @@ +namespace QuanTAlib; + +// Excess kurtosis calculated with Sheskin Algorithm +public class Kurtosis : AbstractBase +{ + public readonly int Period; + private CircularBuffer _buffer; + + public Kurtosis(int period) : base() + { + if (period < 4) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 4 for kurtosis calculation."); + } + Period = period; + WarmupPeriod = Period - 1; + _buffer = new CircularBuffer(period); + Name = $"Kurtosis(period={period})"; + Init(); + } + + public Kurtosis(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double kurtosis = 0; + if (_buffer.Count > 3) + { + var values = _buffer.GetSpan().ToArray(); + double mean = values.Average(); + double n = values.Length; + + double s2 = 0; + double s4 = 0; + + for (int i = 0; i < values.Length; i++) + { + double diff = values[i] - mean; + s2 += diff * diff; + s4 += diff * diff * diff * diff; + } + + double variance = s2 / (n - 1); + + // Using the Sheskin Algorithm for kurtosis + kurtosis = (n * (n + 1) * s4) / (variance * variance * (n - 3) * (n - 1) * (n - 2)) + - (3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3))); + } + + IsHot = _buffer.Count >= Period; + return kurtosis; + } +} diff --git a/lib/statistics/Max.cs b/lib/statistics/Max.cs new file mode 100644 index 00000000..2eccaaaf --- /dev/null +++ b/lib/statistics/Max.cs @@ -0,0 +1,80 @@ +using System; + +namespace QuanTAlib +{ + public class Max : AbstractBase + { + public readonly int Period; + private CircularBuffer _buffer; + private readonly double _halfLife; + private double _currentMax, _p_currentMax; + private int _timeSinceNewMax, _p_timeSinceNewMax; + + public Max(int period, double decay = 0) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + if (decay < 0) + { + throw new ArgumentOutOfRangeException(nameof(decay), "Half-life must be non-negative."); + } + Period = period; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + _halfLife = decay * 0.1; + Name = $"Max(period={period}, halfLife={decay:F2})"; + Init(); + } + + public Max(object source, int period, double decay = 0) : this(period, decay) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _currentMax = double.MinValue; + _timeSinceNewMax = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_currentMax = _currentMax; + _lastValidValue = Input.Value; + _index++; + _timeSinceNewMax++; + _p_timeSinceNewMax = _timeSinceNewMax; + } + else + { + _currentMax = _p_currentMax; + _timeSinceNewMax = _p_timeSinceNewMax; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + if (Input.Value >= _currentMax) + { + _currentMax = Input.Value; + _timeSinceNewMax = 0; + } + + double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period); + _currentMax = _currentMax - decayRate * (_currentMax - _buffer.Average()); + _currentMax = Math.Min(_currentMax, _buffer.Max()); + + IsHot = true; + return _currentMax; + } + } +} \ No newline at end of file diff --git a/lib/statistics/Median.cs b/lib/statistics/Median.cs new file mode 100644 index 00000000..9adb1ed5 --- /dev/null +++ b/lib/statistics/Median.cs @@ -0,0 +1,74 @@ +using System; +using System.Linq; + +namespace QuanTAlib +{ + public class Median : AbstractBase + { + public readonly int Period; + private CircularBuffer _buffer; + + public Median(int period) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + Period = period; + WarmupPeriod = period; + _buffer = new CircularBuffer(period); + Name = $"Median(period={period})"; + Init(); + } + + public Median(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + double median; + if (_index >= Period) + { + var sortedValues = _buffer.GetSpan().ToArray(); + Array.Sort(sortedValues); + int middleIndex = sortedValues.Length / 2; + + if (sortedValues.Length % 2 == 0) + { + median = (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0; + } + else + { + median = sortedValues[middleIndex]; + } + } + else + { + median = _buffer.Average(); // Use average until we have enough data points + } + + IsHot = _index >= WarmupPeriod; + return median; + } + } +} \ No newline at end of file diff --git a/lib/statistics/Min.cs b/lib/statistics/Min.cs new file mode 100644 index 00000000..eb842a16 --- /dev/null +++ b/lib/statistics/Min.cs @@ -0,0 +1,80 @@ +using System; + +namespace QuanTAlib +{ + public class Min : AbstractBase + { + public readonly int Period; + private CircularBuffer _buffer; + private readonly double _halfLife; + private double _currentMin, _p_currentMin; + private int _timeSinceNewMin, _p_timeSinceNewMin; + + public Min(int period, double decay = 0) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + if (decay < 0) + { + throw new ArgumentOutOfRangeException(nameof(decay), "Half-life must be non-negative."); + } + Period = period; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + _halfLife = decay * 0.1; + Name = $"Min(period={period}, halfLife={decay:F2})"; + Init(); + } + + public Min(object source, int period, double decay = 0) : this(period, decay) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _currentMin = double.MaxValue; + _timeSinceNewMin = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _p_currentMin = _currentMin; + _lastValidValue = Input.Value; + _index++; + _timeSinceNewMin++; + _p_timeSinceNewMin = _timeSinceNewMin; + } + else + { + _currentMin = _p_currentMin; + _timeSinceNewMin = _p_timeSinceNewMin; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + if (Input.Value <= _currentMin) + { + _currentMin = Input.Value; + _timeSinceNewMin = 0; + } + + double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period); + _currentMin = _currentMin + decayRate * (_buffer.Average() - _currentMin); + _currentMin = Math.Max(_currentMin, _buffer.Min()); + + IsHot = true; + return _currentMin; + } + } +} \ No newline at end of file diff --git a/lib/statistics/Mode.cs b/lib/statistics/Mode.cs new file mode 100644 index 00000000..3aacd879 --- /dev/null +++ b/lib/statistics/Mode.cs @@ -0,0 +1,70 @@ +namespace QuanTAlib; + +public class Mode : AbstractBase +{ + public readonly int Period; + private CircularBuffer _buffer; + + public Mode(int period) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + Period = period; + WarmupPeriod = period; + _buffer = new CircularBuffer(period); + Name = $"Mode(period={period})"; + Init(); + } + + public Mode(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + double mode; + if (_index >= Period) + { + var values = _buffer.GetSpan().ToArray(); + var groupedValues = values.GroupBy(v => v) + .OrderByDescending(g => g.Count()) + .ThenBy(g => g.Key) + .ToList(); + + int maxCount = groupedValues.First().Count(); + var modes = groupedValues.TakeWhile(g => g.Count() == maxCount) + .Select(g => g.Key) + .ToList(); + + mode = modes.Average(); // If there are multiple modes, we return their average + } + else + { + mode = _buffer.Average(); // Use average until we have enough data points + } + + IsHot = _index >= WarmupPeriod; + return mode; + } +} diff --git a/lib/statistics/Percentile.cs b/lib/statistics/Percentile.cs new file mode 100644 index 00000000..d90afe0e --- /dev/null +++ b/lib/statistics/Percentile.cs @@ -0,0 +1,88 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +public class Percentile : AbstractBase +{ + public readonly int Period; + public readonly double Percent; + private CircularBuffer _buffer; + + public Percentile(int period, double percent) : base() + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for percentile calculation."); + } + if (percent < 0 || percent > 100) + { + throw new ArgumentOutOfRangeException(nameof(percent), "Percent must be between 0 and 100."); + } + Period = period; + Percent = percent; + WarmupPeriod = 2; + _buffer = new CircularBuffer(period); + Name = $"Percentile(period={period}, percent={percent})"; + Init(); + } + + public Percentile(object source, int period, double percent) : this(period, percent) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + +protected override double Calculation() + { + ManageState(Input.IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + double result; + if (_buffer.Count >= Period) + { + var values = _buffer.GetSpan().ToArray(); + Array.Sort(values); + + double position = (Percent / 100.0) * (values.Length - 1); + int lowerIndex = (int)Math.Floor(position); + int upperIndex = (int)Math.Ceiling(position); + + if (lowerIndex == upperIndex) + { + result = values[lowerIndex]; + } + else + { + // Interpolate between the two nearest values + double lowerValue = values[lowerIndex]; + double upperValue = values[upperIndex]; + double fraction = position - lowerIndex; + result = lowerValue + (upperValue - lowerValue) * fraction; + } + } + else + { + // Use average for insufficient data, like the Median class + result = _buffer.Average(); + } + + IsHot = _buffer.Count >= Period; + return result; + } +} \ No newline at end of file diff --git a/lib/statistics/Skew.cs b/lib/statistics/Skew.cs new file mode 100644 index 00000000..f7aaeec8 --- /dev/null +++ b/lib/statistics/Skew.cs @@ -0,0 +1,82 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +public class Skew : AbstractBase +{ + public readonly int Period; + private CircularBuffer _buffer; + + public Skew(int period) : base() + { + if (period < 3) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 3 for skewness calculation."); + } + Period = period; + WarmupPeriod = 3; + _buffer = new CircularBuffer(period); + Name = $"Skew(period={period})"; + Init(); + } + + public Skew(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double skew = 0; + if (_buffer.Count >= 3) // We need at least 3 data points for skewness + { + var values = _buffer.GetSpan().ToArray(); + double mean = values.Average(); + double n = values.Length; + + double sumCubedDeviations = 0; + double sumSquaredDeviations = 0; + + foreach (var value in values) + { + double deviation = value - mean; + sumCubedDeviations += Math.Pow(deviation, 3); + sumSquaredDeviations += Math.Pow(deviation, 2); + } + + // Calculate sample skewness using the adjusted Fisher-Pearson standardized moment coefficient + double m3 = sumCubedDeviations / n; + double m2 = sumSquaredDeviations / n; + double s3 = Math.Pow(m2, 1.5); + + if (s3 != 0) // Avoid division by zero + { + skew = (Math.Sqrt(n * (n - 1)) / (n - 2)) * (m3 / s3); + } + } + + IsHot = _buffer.Count >= Period; + return skew; + } +} \ No newline at end of file diff --git a/lib/statistics/Stddev.cs b/lib/statistics/Stddev.cs new file mode 100644 index 00000000..d1eff641 --- /dev/null +++ b/lib/statistics/Stddev.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; + +namespace QuanTAlib +{ + public class Stddev : AbstractBase + { + public readonly int Period; + public readonly bool IsPopulation; + private CircularBuffer _buffer; + + public Stddev(int period, bool isPopulation = false) : base() + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + Period = period; + IsPopulation = isPopulation; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + Name = $"Stddev(period={period}, population={isPopulation})"; + Init(); + } + + public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double stddev = 0; + if (_buffer.Count > 1) + { + var values = _buffer.GetSpan().ToArray(); + double mean = values.Average(); + double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2)); + + double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1; + double variance = sumOfSquaredDifferences / divisor; + stddev = Math.Sqrt(variance); + } + + IsHot = true; // StdDev calc is valid from bar 1 + return stddev; + } + } +} \ No newline at end of file diff --git a/lib/statistics/Variance.cs b/lib/statistics/Variance.cs new file mode 100644 index 00000000..675958ca --- /dev/null +++ b/lib/statistics/Variance.cs @@ -0,0 +1,68 @@ +using System; +using System.Linq; + +namespace QuanTAlib +{ + public class Variance : AbstractBase + { + public readonly int Period; + public readonly bool IsPopulation; + private CircularBuffer _buffer; + + public Variance(int period, bool isPopulation = false) : base() + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + Period = period; + IsPopulation = isPopulation; + WarmupPeriod = 0; + _buffer = new CircularBuffer(period); + Name = $"Variance(period={period}, population={isPopulation})"; + Init(); + } + + public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double variance = 0; + if (_buffer.Count > 1) + { + var values = _buffer.GetSpan().ToArray(); + double mean = values.Average(); + double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2)); + + double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1; + variance = sumOfSquaredDifferences / divisor; + } + + IsHot = true; + return variance; + } + } +} \ No newline at end of file diff --git a/lib/statistics/Zscore.cs b/lib/statistics/Zscore.cs new file mode 100644 index 00000000..87f59b67 --- /dev/null +++ b/lib/statistics/Zscore.cs @@ -0,0 +1,70 @@ +namespace QuanTAlib; + +using System; +using System.Linq; + +public class Zscore : AbstractBase +{ + public readonly int Period; + private CircularBuffer _buffer; + + public Zscore(int period) : base() + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for Z-score calculation."); + } + Period = period; + WarmupPeriod = 2; + _buffer = new CircularBuffer(period); + Name = $"ZScore(period={period})"; + Init(); + } + + public Zscore(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + double zScore = 0; + if (_buffer.Count >= 2) // We need at least 2 data points for Z-score + { + var values = _buffer.GetSpan().ToArray(); + double mean = values.Average(); + double n = values.Length; + + double sumSquaredDeviations = values.Sum(x => Math.Pow(x - mean, 2)); + double standardDeviation = Math.Sqrt(sumSquaredDeviations / (n - 1)); // Sample standard deviation + + if (standardDeviation != 0) // Avoid division by zero + { + zScore = (Input.Value - mean) / standardDeviation; + } + } + + IsHot = _buffer.Count >= Period; + return zScore; + } +} \ No newline at end of file diff --git a/notebooks/Htit.dib b/notebooks/Htit.dib new file mode 100644 index 00000000..403113e0 --- /dev/null +++ b/notebooks/Htit.dib @@ -0,0 +1,198 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\src\obj\Debug\QuanTAlib.dll" + +#r "nuget:Skender.Stock.Indicators" + +using Skender.Stock.Indicators; +using QuanTAlib; + +#!csharp + + public class Htit : AbstractBase + { + private readonly int _period; + private readonly CircularBuffer _pr, _sp, _dt, _pd, _q1, _i1, _q2, _i2, _re, _im, _sd, _it; + + public Htit(int period = 50) : base() + { + _period = period; + _pr = new CircularBuffer(period); + _sp = new CircularBuffer(period); + _dt = new CircularBuffer(period); + _pd = new CircularBuffer(period); + _q1 = new CircularBuffer(period); + _i1 = new CircularBuffer(period); + _q2 = new CircularBuffer(period); + _i2 = new CircularBuffer(period); + _re = new CircularBuffer(period); + _im = new CircularBuffer(period); + _sd = new CircularBuffer(period); + _it = new CircularBuffer(period); + Name = "Htit"; + WarmupPeriod = 12; // Minimum required data points + Init(); + } + + public Htit(object source, int period = 50) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _pr.Clear(); + _sp.Clear(); + _dt.Clear(); + _pd.Clear(); + _q1.Clear(); + _i1.Clear(); + _q2.Clear(); + _i2.Clear(); + _re.Clear(); + _im.Clear(); + _sd.Clear(); + _it.Clear(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + } + } + + protected override double GetLastValid() + { + return _it[^1]; + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + _pr.Add(Input.Value, Input.IsNew); + + if (_index > 6) + { + double adj = (0.075 * _pd[^2]) + 0.54; + + // smooth and detrender + _sp.Add(((4 * _pr[^1]) + (3 * _pr[^2]) + (2 * _pr[^3]) + _pr[^4]) / 10, Input.IsNew); + _dt.Add(((0.0962 * _sp[^1]) + (0.5769 * _sp[^3]) - (0.5769 * _sp[^5]) - (0.0962 * _sp[^7])) * adj, Input.IsNew); + + // in-phase and quadrature + _q1.Add(((0.0962 * _dt[^1]) + (0.5769 * _dt[^3]) - (0.5769 * _dt[^5]) - (0.0962 * _dt[^7])) * adj, Input.IsNew); + _i1.Add(_dt[^4], Input.IsNew); + + // advance the phases by 90 degrees + double jI = ((0.0962 * _i1[^1]) + (0.5769 * _i1[^3]) - (0.5769 * _i1[^5]) - (0.0962 * _i1[^7])) * adj; + double jQ = ((0.0962 * _q1[^1]) + (0.5769 * _q1[^3]) - (0.5769 * _q1[^5]) - (0.0962 * _q1[^7])) * adj; + + // phasor addition for 3-bar averaging + _i2.Add(_i1[^1] - jQ, Input.IsNew); + _q2.Add(_q1[^1] + jI, Input.IsNew); + + _i2[^1] = (0.2 * _i2[^1]) + (0.8 * _i2[^2]); // smoothing it + _q2[^1] = (0.2 * _q2[^1]) + (0.8 * _q2[^2]); + + // homodyne discriminator + _re.Add((_i2[^1] * _i2[^2]) + (_q2[^1] * _q2[^2]), Input.IsNew); + _im.Add((_i2[^1] * _q2[^2]) - (_q2[^1] * _i2[^2]), Input.IsNew); + + _re[^1] = (0.2 * _re[^1]) + (0.8 * _re[^2]); // smoothing it + _im[^1] = (0.2 * _im[^1]) + (0.8 * _im[^2]); + + // calculate period + _pd.Add(_im[^1] != 0 && _re[^1] != 0 + ? 2 * Math.PI / Math.Atan(_im[^1] / _re[^1]) + : 0, Input.IsNew); + + // adjust period to thresholds + _pd[^1] = (_pd[^1] > 1.5 * _pd[^2]) ? 1.5 * _pd[^2] : _pd[^1]; + _pd[^1] = (_pd[^1] < 0.67 * _pd[^2]) ? 0.67 * _pd[^2] : _pd[^1]; + _pd[^1] = (_pd[^1] < 6.0) ? 6.0 : _pd[^1]; + _pd[^1] = (_pd[^1] > 50.0) ? 50.0 : _pd[^1]; + + // smooth the period + _pd[^1] = (0.2 * _pd[^1]) + (0.8 * _pd[^2]); + _sd.Add((0.33 * _pd[^1]) + (0.67 * _sd[^2]), Input.IsNew); + +//check this loop + // smooth dominant cycle period + int dcPeriods = (int)(_sd[^1] + 0.5); + double sumPr = 0; + for (int d = 1; d < dcPeriods+1; d++) //0 -> 5 + { + sumPr += _pr[^d]; + } + _it.Add(dcPeriods > 0 ? sumPr / dcPeriods : _pr[^1], Input.IsNew); + + +Console.WriteLine($"{_index}\t {_it[^1]:F2}"); + + // final indicators + double Trendline, SmoothPrice; + + Trendline = _index >= 12 // 12th bar + ? ((4 * _it[^1]) + (3 * _it[^2]) + (2 * _it[^3]) + _it[^4]) / 10.0 + : _pr[^1]; + SmoothPrice = ((4 * _pr[^1]) + (3 * _pr[^2]) + (2 * _pr[^3]) + _pr[^4]) / 10.0; + + Value = Trendline; + } + + else + { + Value = _pr[^1]; + _pd.Add(0, Input.IsNew); + _sp.Add(0, Input.IsNew); + _dt.Add(0, Input.IsNew); + _i1.Add(0, Input.IsNew); + _q1.Add(0, Input.IsNew); + _i2.Add(0, Input.IsNew); + _q2.Add(0, Input.IsNew); + _re.Add(0, Input.IsNew); + _im.Add(0, Input.IsNew); + _sd.Add(0, Input.IsNew); + _it.Add(_pr[^1], Input.IsNew); + } + + IsHot = _index >= WarmupPeriod; + return Value; + } + } + +#!csharp + +Random rnd = new((int)DateTime.Now.Ticks); +GbmFeed feed = new(sigma: 0.5, mu: 0.0); + +TBarSeries bars = new(feed); +feed.Add(15); + +IEnumerable quotes = feed.Select(q => new Quote { + Date = q.Time, + Open = (decimal)q.Open, + High = (decimal)q.High, + Low = (decimal)q.Low, + Close = (decimal)q.Close, + Volume = (decimal)q.Volume +}); + +Htit ma = new(); +TSeries QL = new(); +foreach (TBar item in feed) { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } +var SK = quotes.Select(q => (q.Date, (double)q.Close)).GetHtTrendline().Select(i => i.Trendline.Null2NaN()!); + +Console.WriteLine($"Data\tSkend\tQuanTAlib"); +for (int i = 8; i < feed.Length; i++) +{ + Console.WriteLine($"{i}\t{feed[i].Close,6:F2}\t{SK.ElementAt(i),6:F2}\t{QL[i].Value,6:F2} {Math.Truncate(SK.ElementAt(i)*100)==Math.Truncate(QL[i].Value*100)}"); +} diff --git a/notebooks/Tulip.dib b/notebooks/Tulip.dib new file mode 100644 index 00000000..1fe572c1 --- /dev/null +++ b/notebooks/Tulip.dib @@ -0,0 +1,28 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "nuget: Tulip.NETCore, 0.8.0.1" + +#!csharp + +using Tulip; + +double[] data = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; +int period = 3; +private double[] outdata = new double[data.Count()]; + +double[][] arrin = new double[][] { data }; +double[][] arrout = new double[][] { outdata }; +Tulip.Indicators.ema.Run(inputs: arrin, options: new double[] { period }, outputs: arrout); + +for (int i=0; i +{ + private readonly double[] _buffer; + private int _start = 0; + private int _size = 0; + + public int Capacity { get; } + public int Count => _size; + + public CircularBuffer(int capacity) + { + Capacity = capacity; + _buffer = GC.AllocateArray(capacity, pinned: true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(double item, bool isNew = true) + { + if (_size == 0 || isNew) + { + if (_size < Capacity) + { + _buffer[(_start + _size) % Capacity] = item; + _size++; + } + else + { + _buffer[_start] = item; + _start = (_start + 1) % Capacity; + } + } + else + { + _buffer[(_start + _size - 1) % Capacity] = item; + } + } + + public double this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + index = index < 0 ? 0 : (index >= _size ? _size - 1 : index); + return _buffer[(_start + index) % Capacity]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + index = index < 0 ? 0 : (index >= _size ? _size - 1 : index); + _buffer[(_start + index) % Capacity] = value; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowArgumentOutOfRangeException() + { + throw new ArgumentOutOfRangeException("index", "Index is out of range."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Newest() + { + if (_size == 0) + return 0; + return _buffer[(_start + _size - 1) % Capacity]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Oldest() + { + if (_size == 0) + ThrowInvalidOperationException(); + return _buffer[_start]; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInvalidOperationException() + { + throw new InvalidOperationException("Buffer is empty."); + } + + public Enumerator GetEnumerator() => new(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator : IEnumerator + { + private readonly CircularBuffer _buffer; + private int _index; + private double _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(CircularBuffer buffer) + { + _buffer = buffer; + _index = -1; + _current = default; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (_index + 1 >= _buffer._size) + return false; + + _index++; + _current = _buffer[_index]; + return true; + } + + public double Current => _current; + object IEnumerator.Current => Current; + + public void Reset() + { + _index = -1; + _current = default; + } + + public void Dispose() { } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(double[] destination, int destinationIndex) + { + if (_size == 0) + return; + + if (_start + _size <= Capacity) + { + Array.Copy(_buffer, _start, destination, destinationIndex, _size); + } + else + { + int firstPartLength = Capacity - _start; + Array.Copy(_buffer, _start, destination, destinationIndex, firstPartLength); + Array.Copy(_buffer, 0, destination, destinationIndex + firstPartLength, _size - firstPartLength); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan GetSpan() + { + if (_size == 0) + return ReadOnlySpan.Empty; + + if (_start + _size <= Capacity) + { + return new ReadOnlySpan(_buffer, _start, _size); + } + else + { + return new ReadOnlySpan(ToArray()); + } + } + + public double[] InternalBuffer => _buffer; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan GetInternalSpan() => _buffer.AsSpan(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + Array.Clear(_buffer, 0, _buffer.Length); + _start = 0; + _size = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Max() + { + if (_size == 0) + ThrowInvalidOperationException(); + + return MaxSimd(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Min() + { + if (_size == 0) + ThrowInvalidOperationException(); + + return MinSimd(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Sum() + { + return SumSimd(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Average() + { + if (_size == 0) + ThrowInvalidOperationException(); + + return SumSimd() / _size; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double MaxSimd() + { + var span = GetSpan(); + var vectorSize = Vector.Count; + var maxVector = new Vector(double.MinValue); + + int i = 0; + for (; i <= span.Length - vectorSize; i += vectorSize) + { + maxVector = Vector.Max(maxVector, new Vector(span.Slice(i, vectorSize))); + } + + double max = double.MinValue; + for (int j = 0; j < vectorSize; j++) + { + max = Math.Max(max, maxVector[j]); + } + + for (; i < span.Length; i++) + { + max = Math.Max(max, span[i]); + } + + return max; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double MinSimd() + { + var span = GetSpan(); + var vectorSize = Vector.Count; + var minVector = new Vector(double.MaxValue); + + int i = 0; + for (; i <= span.Length - vectorSize; i += vectorSize) + { + minVector = Vector.Min(minVector, new Vector(span.Slice(i, vectorSize))); + } + + double min = double.MaxValue; + for (int j = 0; j < vectorSize; j++) + { + min = Math.Min(min, minVector[j]); + } + + for (; i < span.Length; i++) + { + min = Math.Min(min, span[i]); + } + + return min; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double SumSimd() + { + var span = GetSpan(); + var vectorSize = Vector.Count; + var sumVector = Vector.Zero; + + int i = 0; + for (; i <= span.Length - vectorSize; i += vectorSize) + { + sumVector += new Vector(span.Slice(i, vectorSize)); + } + + double sum = 0; + for (int j = 0; j < vectorSize; j++) + { + sum += sumVector[j]; + } + + for (; i < span.Length; i++) + { + sum += span[i]; + } + + return sum; + } + + public double[] ToArray() + { + double[] array = new double[_size]; + CopyTo(array, 0); + return array; + } + + public void ParallelOperation(Func operation) + { + const int MinimumPartitionSize = 1024; + + if (_size < MinimumPartitionSize) + { + var span = GetSpan(); + var array = span.ToArray(); + operation(array, 0, array.Length); + return; + } + + int partitionCount = Environment.ProcessorCount; + int partitionSize = _size / partitionCount; + + if (partitionSize < MinimumPartitionSize) + { + partitionCount = Math.Max(1, _size / MinimumPartitionSize); + partitionSize = _size / partitionCount; + } + + var buffer = ToArray(); + var results = new double[partitionCount]; + + Parallel.For(0, partitionCount, i => + { + int start = i * partitionSize; + int length = (i == partitionCount - 1) ? _size - start : partitionSize; + results[i] = operation(buffer, start, length); + }); + + } + +} + +#!csharp + +public interface iTValue { + DateTime Time { get; } + double Value { get; } + bool IsNew { get; } + bool IsHot { get; } +} + +public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) : iTValue { + public DateTime Time { get; init; } = Time; + public double Value { get; init; } = Value; + public bool IsNew { get; init; } = IsNew; + public bool IsHot { get; init; } = IsHot; + public DateTime t => Time; + public double v => Value; + + public TValue() : this(DateTime.UtcNow, 0) { } + public TValue(double value, bool isNew=true, bool isHot=true) : this(DateTime.UtcNow, value, IsNew:isNew, IsHot:isHot) { } + public static implicit operator double(TValue tv) => tv.Value; + public static implicit operator DateTime(TValue tv) => tv.Time; + public static implicit operator TValue(double value) => new TValue(DateTime.UtcNow, value); + + public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}, {Value:F2}, IsNew: {IsNew}, IsHot: {IsHot}]"; +} + +public delegate void ValueSignal(object source, in ValueEventArgs args); + +public class ValueEventArgs : EventArgs { + public TValue Tick { get; } + public ValueEventArgs(TValue value) { Tick = value; } +} + +public class TSeries : List { + private readonly TValue Default = new(DateTime.MinValue, double.NaN); + public IEnumerable t => this.Select(item => item.t); + public IEnumerable v => this.Select(item => item.v); + public TValue Last => Count > 0 ? this[^1] : Default; + public TValue First => Count > 0 ? this[0] : Default; + public int Length => Count; + public string Name { get; set; } + public event ValueSignal Pub = delegate { }; + + public TSeries() { this.Name = "Data"; } + + public TSeries (object source) : this() { + var pubEvent = source.GetType().GetEvent("Pub"); + if (pubEvent != null) { + /* + var nameProperty = source.GetType().GetProperty("Name"); + if (nameProperty != null) { + Name = nameProperty.GetValue(nameProperty)?.ToString()!; + } + */ + pubEvent.AddEventHandler(source, new ValueSignal(Sub)); + } + } + public static explicit operator List(TSeries series) => series.Select(item => item.Value).ToList(); + public static explicit operator double[](TSeries series) => series.Select(item => item.Value).ToArray(); + + public new virtual void Add(TValue tick) { + if (tick.IsNew) { base.Add(tick); } + else { this[^1] = tick; } + Pub?.Invoke(this, new ValueEventArgs(tick)); + } + public virtual void Add(DateTime Time, double Value, bool IsNew=true, bool IsHot=true) => this.Add(new TValue(Time, Value, IsNew, IsHot)); + public virtual void Add(double Value, bool IsNew=true, bool IsHot=true) => this.Add(new TValue(DateTime.UtcNow, Value, IsNew, IsHot)); + + public void Add(IEnumerable values) { + var valueList = values.ToList(); + int count = valueList.Count; + DateTime startTime = DateTime.UtcNow - TimeSpan.FromHours(count); + + for (int i = 0; i < count; i++) { + this.Add(startTime, valueList[i]); + startTime = startTime.AddHours(1); + } + } + public void Add(TSeries series) { + if (series == this) { + // If adding itself, create a copy to avoid modification during enumeration + var copy = new TSeries { Name = this.Name }; + copy.AddRange(this); + AddRange(copy); + } else { + AddRange(series); + } + } + public new virtual void AddRange(IEnumerable collection) { + foreach (var item in collection) { + Add(item); + } + } + public void Sub(object source, in ValueEventArgs args) { Add(args.Tick); } +} + +#!csharp + +TValue a = new(10.0); +TSeries ll = new(); +ll.Add(a); +ll.Add(10); +TSeries ll1 = new(); +ll.Add(new double[]{1, 2, 3, 4}); +ll.Add(new List{1, 2, 3, 4}); +ll.Add(ll); + +display((double[])ll); + +#!csharp + +public interface iTBar { + DateTime Time { get; } + double Open { get; } + double High { get; } + double Low { get; } + double Close { get; } + double Volume { get; } + bool IsNew { get; } +} + +public readonly record struct TBar(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) :iTBar { + public DateTime Time { get; init; } = Time; + public double Open { get; init; } = Open; + public double High { get; init; } = High; + public double Low { get; init; } = Low; + public double Close { get; init; } = Close; + public double Volume { get; init; } = Volume; + public bool IsNew { get; init; } = IsNew; + + public double HL2 => (High + Low) * 0.5; + public double OC2 => (Open + Close) * 0.5; + public double OHL3 => (Open + High + Low) /3; + public double HLC3 => (High + Low + Close) /3; + public double OHLC4 => (Open + High + Low + Close) * 0.25; + public double HLCC4 => (High + Low + Close + Close) * 0.25; + + public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { } + public TBar(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : this(DateTime.UtcNow, Open, High, Low, Close, Volume, IsNew) { } + + // when TBar casts to double, it returns its Close + public static implicit operator double(TBar bar) => bar.Close; + public static implicit operator DateTime(TBar tv) => tv.Time; + + // castings for sloppy people - a single double injected into a TBar, and a single TValue injected into a TBar + public TBar (double value) : this(Time: DateTime.UtcNow, Open: value, High: value, Low: value, Close: value, Volume: value, IsNew: true) {} + public TBar (TValue value) : this(Time: value.Time, Open: value.Value, High: value.Value, Low: value.Value, Close: value.Value, Volume: value.Value, IsNew: value.IsNew) {} + + public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]"; +} + +public delegate void BarSignal(object source, in TBarEventArgs args); + +public class TBarEventArgs : EventArgs { + public TBar Bar { get; } + public TBarEventArgs(TBar bar) { Bar = bar; } +} + +public class TBarSeries : List { + private readonly TBar Default = new(DateTime.MinValue, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN); + + public TSeries Open; + public TSeries High; + public TSeries Low; + public TSeries Close; + public TSeries Volume; + + + public TBar Last => Count > 0 ? this[^1] : Default; + public TBar First => Count > 0 ? this[0] : Default; + public int Length => Count; + public string Name { get; set; } + public event BarSignal Pub = delegate { }; + + public TBarSeries() { + this.Name = "Bar"; + Open = new(); + High = new(); + Low = new(); + Close = new(); + Volume = new(); + } + public TBarSeries (object source) : this() { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new BarSignal(Sub)); + } + + public new virtual void Add(TBar bar) { + if (bar.IsNew) { base.Add(bar); }else { this[^1] = bar; } + Pub?.Invoke(this, new TBarEventArgs(bar)); + + Open.Add(bar.Time, bar.Open, IsNew: bar.IsNew, IsHot: true); + High.Add(bar.Time, bar.High, IsNew: bar.IsNew, IsHot: true); + Low.Add(bar.Time, bar.Low, IsNew: bar.IsNew, IsHot: true); + Close.Add(bar.Time, bar.Close, IsNew: bar.IsNew, IsHot: true); + Volume.Add(bar.Time, bar.Volume, IsNew: bar.IsNew, IsHot: true); + } + public void Add(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) => + this.Add(new TBar(Time, Open, High, Low, Close, Volume, IsNew)); + + public void Add(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) => + this.Add(new TBar(DateTime.Now, Open, High, Low, Close, Volume, IsNew)); + + public void Add(TBarSeries series) { + if (series == this) { + // If adding itself, create a copy to avoid modification during enumeration + var copy = new TBarSeries { Name = this.Name }; + copy.AddRange(this); + AddRange(copy); + } else { + AddRange(series); + } + } + public new virtual void AddRange(IEnumerable collection) { + foreach (var item in collection) { + Add(item); + } + } + + public void Sub(object source, in TBarEventArgs args) { + Add(args.Bar); + } +} + +#!csharp + +TBarSeries ll = new(); +ll.Add(1,2,3,4,5); +ll.Add(1,2,3,4,5); +ll.Add(1,2,3,4,5); +ll.Add(ll); +//ll.Add(a); +//ll.Add(10); +//TSeries ll1 = new(); +//ll.Add(new double[]{1, 2, 3, 4}); +//ll.Add(new List{1, 2, 3, 4}); +//ll.Add(ll); + +display(ll.Open.Last.Value); + +#!csharp + +using System; +using System.Collections.Generic; +using System.CommandLine.Invocation; + +public abstract class AbstractBase : iTValue +{ + public DateTime Time { get; set; } + public double Value { get; set; } + public bool IsNew { get; set; } + public bool IsHot { get; set; } + + public TValue Input { get; set; } + + public TValue Tick => new(Time, Value, IsNew, IsHot); // Stores the current value of indicator + public event ValueSignal Pub = delegate { }; // Publisher of generated values + + protected int _index; //tracking the position of output + protected double _lastValidValue; + // other _internal vars defined here + + protected AbstractBase() + { //add parameters into constructor + } + + public void Sub(object source, in ValueEventArgs args) => Calc(args.Tick); + + public virtual void Init() + { + _index = 0; + _lastValidValue = 0; + } + + public virtual TValue Calc(TValue input) + { + Input = input; + if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) + { + return Process(new TValue(input.Time, GetLastValid(), input.IsNew, input.IsHot)); + } + this.Value = Calculation(); + return Process(new TValue(Time: Input.Time, Value: this.Value, IsNew: Input.IsNew, IsHot: this.IsHot)); + } + + protected virtual double GetLastValid() + { + // should return last valid value + return _lastValidValue; + } + protected abstract void ManageState(bool isNew); + protected abstract double Calculation(); + protected virtual TValue Process(TValue value) + { + this.Time = value.Time; + this.Value = value.Value; + this.IsNew = value.IsNew; + this.IsHot = value.IsHot; + Pub?.Invoke(this, new ValueEventArgs(value)); + return value; + } +} + +#!csharp + +using System; + + public class EmaCalc : AbstractBase + { + private readonly int _period; + private CircularBuffer _sma; + private double _lastEma, _p_lastEma; + private double _k, _e, _p_e; + private bool _isInitialized, _useSma; + + public EmaCalc(int period, bool useSma = true) : base() + { + if (period < 1) { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _period = period; + _useSma = useSma; + _sma = new(period); + + Init(); + } + + public EmaCalc(object source, int period, bool useSma = true) : this(period, useSma) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _k = 2.0 / (_period + 1); + _e = 1.0; + _lastEma = 0; + _isInitialized = false; + _sma = new(_period); + } + + protected override void ManageState(bool isNew) { + if (isNew) { + _p_lastEma = _lastEma; + _p_e = _e; + _index++; + } else { + _lastEma = _p_lastEma; + _e = _p_e; + } + } + + protected override double GetLastValid() { + return _lastEma; + } + + protected override double Calculation() { + double result, _ema; + ManageState(Input.IsNew); + + // when _UseSma == true, use SMA calculation until we have enough data points + if (!_isInitialized && _useSma) { + _sma.Add(Input.Value, Input.IsNew); + _ema = _sma.Average(); + result = _ema; + if (_index >= _period) { + _isInitialized = true; + } + } else { + // dunamic k when within period; (index is zero-based, therefore +2) + double _dk = (_index +1 >= _period) ? _k : 2.0 / (_index + 2); + + // compensator for early ema values + _e = (_e > 1e-10) ? (1 - _dk) * _e : 0; + + _ema = _dk * (Input.Value - _lastEma) + _lastEma; + + // _useSma decides if we use compensator or not + result = (_useSma || _e == 0)? _ema : _ema / (1 - _e); + } + + _lastEma = _ema; + IsHot = _index >= _period; + return result; + } + } + +#!csharp + +double[] input = new[]{1.0, 2,3,4,5}; + +TSeries mm = new(); +mm.Add(input); +mm.Display(); + +#!csharp + +public class Convolution : AbstractBase + { + private readonly double[] _kernel; + private readonly int _kernelSize; + private CircularBuffer _buffer; + private double[] _normalizedKernel; + + public Convolution(double[] kernel) + { + if (kernel == null || kernel.Length == 0) + { + throw new ArgumentException("Kernel must not be null or empty.", nameof(kernel)); + } + _kernel = kernel; + _kernelSize = kernel.Length; + _buffer = new CircularBuffer(_kernelSize); + _normalizedKernel = new double[_kernelSize]; + Init(); + } + + public Convolution(object source, double[] kernel) : this(kernel) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private void Init() + { + _index = 0; + _lastValidValue = 0; + Array.Copy(_kernel, _normalizedKernel, _kernelSize); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double GetLastValid() + { + return _lastValidValue; + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + _buffer.Add(Input.Value, Input.IsNew); + + // Normalize kernel on each calculation until buffer is full + if (_index <= _kernelSize) + { + NormalizeKernel(); + } + + double result = ConvolveBuffer(); + IsHot = _index >= _kernelSize; + + return result; + } + + private void NormalizeKernel() + { + int activeLength = Math.Min(_index, _kernelSize); + double sum = 0; + + // Calculate the sum of the active kernel elements + for (int i = 0; i < activeLength; i++) + { + sum += _kernel[i]; + } + + // Normalize the kernel or set equal weights if the sum is zero + double normalizationFactor = (sum != 0) ? sum : activeLength; + for (int i = 0; i < activeLength; i++) + { + _normalizedKernel[i] = _kernel[i] / normalizationFactor; + } + + // Set the rest of the normalized kernel to zero + Array.Clear(_normalizedKernel, activeLength, _kernelSize - activeLength); + } + + private double ConvolveBuffer() + { + double sum = 0; + var bufferSpan = _buffer.GetSpan(); + int activeLength = Math.Min(_index, _kernelSize); + + for (int i = 0; i < activeLength; i++) + { + sum += bufferSpan[activeLength - 1 - i] * _normalizedKernel[i]; + } + + return sum; + } + } + +#!csharp + +public class Wma : AbstractBase + { + private readonly int _period; + private readonly Convolution _convolution; + + public Wma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateWmaKernel(_period)); + Init(); + } + + public Wma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private static double[] GenerateWmaKernel(int period) + { + double[] kernel = new double[period]; + double weightSum = period * (period + 1) / 2.0; + + for (int i = 0; i < period; i++) + { + kernel[i] = (period - i) / weightSum; + } + + return kernel; + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double GetLastValid() + { + return _lastValidValue; + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + IsHot = _index >= _period; + + return result; + } + } + +#!csharp + +TSeries input = new(); +double[] kernel = new[]{4.0,3,2,1}; + +Wma cc = new(input, 5); +TSeries output = new(cc); + +input.Add(new double[]{1.0,2,3,4,5,6,7,8}); + + +display((double[])output); diff --git a/notebooks/charting.dib b/notebooks/charting.dib new file mode 100644 index 00000000..93f47139 --- /dev/null +++ b/notebooks/charting.dib @@ -0,0 +1,82 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\lib\obj\Debug\QuanTAlib.dll" +using QuanTAlib; +QuanTAlib.Formatters.Initialize(); + +#!csharp + +TSeries Spike = new() { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +TSeries Impulse = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }; +TSeries Triangle = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 }; +TSeries Sawtooth = new() { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +TSeries Sine = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 }; +TSeries Chirp = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 }; +TSeries White = new() { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09}; +TSeries Gauss = new() { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61}; +TSeries B = new() { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06}; +TSeries HF = new() { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86}; +TSeries ImpulseHF = new() { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71}; +TSeries SawtoothHF = new() { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3}; +TSeries SineG = new() { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35}; +TSeries ChirpG = new() { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58}; +TSeries Complex = new() { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83}; +TSeries Market = new() { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25}; + +#!csharp + +#r "nuget: ScottPlot" + +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +TSeries ma1 = Spike; +TSeries out1 = new(); +Ema calc1 = new(10); +foreach (var value in ma1) { out1.Add(calc1.Calc(value)); } +double[] gma1 = ma1.v.ToArray()[52..]; +double[] gsig1 = out1.v.ToArray()[52..]; + +TSeries ma2 = Impulse; +TSeries out2 = new(); +Ema calc2 = new(10); +foreach (var value in ma2) { out2.Add(calc2.Calc(value)); } +double[] gma2 = ma2.v.ToArray()[52..]; +double[] gsig2 = out2.v.ToArray()[52..]; + +#!csharp + +Plot plt1 = new(); +var p1a = plt1.Add.Signal(gma1); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; +var p1b = plt1.Add.Signal(gsig1); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 3; +plt1.Title("Spike - EMA(10)"); + +Plot plt2 = new(); +var p2a = plt2.Add.Signal(gma2); p2a.Color = ScottPlot.Colors.Red; p2a.LineWidth = 2; +var p2b = plt2.Add.Signal(gsig2); p2b.Color = ScottPlot.Colors.Blue; p2b.LineWidth = 3; +plt2.Title("Impulse - EMA(10)"); + +plt1.Display(); +plt2.Display(); + +#!csharp + +#r "nuget: Plotly.net.Interactive" +#r "nuget: Plotly.NET.CSharp" + +using Plotly.NET.Interactive; +using Plotly.NET.CSharp; + +#!csharp + +var ch1 = Chart.Line( x: Enumerable.Range(0,gsig.Count()), y: gsig, Name: "signal"); +var ch2 = Chart.Line( x: Enumerable.Range(0,gma.Count()), y: gma); +Chart.Combine(new[] {ch1,ch2}).Display(); diff --git a/notebooks/core.dib b/notebooks/core.dib new file mode 100644 index 00000000..2f997b49 --- /dev/null +++ b/notebooks/core.dib @@ -0,0 +1,125 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\src\obj\Debug\QuanTAlib.dll" + +#r "nuget:Skender.Stock.Indicators" +using Skender.Stock.Indicators; +using QuanTAlib; +QuanTAlib.Formatters.Initialize(); + +#!csharp + +GbmFeed gbm = new(); +EmaCalc ema1 = new(gbm.Close, 10, useSma: false); +EmaCalc ema2 = new(gbm.Close, 10, useSma: true); +TValSeries res1 = new(ema1); +TValSeries res2 = new(ema2); +gbm.Add(50); +List mse1 = new(); +List mse2 = new(); + + +for (int i=0; i< gbm.Length; i++) { + double v= gbm.Close[i].Value; + double e1 = res1[i].Value; + mse1.Add((e1-v)*(e1-v)); + double e2 = res2[i].Value; + mse2.Add((e2-v)*(e2-v)); + + //Console.WriteLine($"{i,3} {mse1.Average(),10:F4} {mse2.Average(),10:F4}"); +} + + Console.WriteLine($"{mse2.Average()-mse1.Average(),10:F8}"); + +#!csharp + +display(res1); + +#!csharp + +GbmFeed gbm = new(); +EmaCalc ema1 = new(gbm.Close, 10, useSma: false); +EmaCalc ema2 = new(gbm.Close, 10, useSma: true); +TValSeries res1 = new(ema1); +TValSeries res2 = new(ema2); +gbm.Add(30); +IEnumerable quotes = gbm.Close.Select(item => new Quote { Date = item.Time, Close = (decimal)item.Value }); +var SkResults = quotes.GetEma(10).Select(i => i.Ema.Null2NaN()!); +for (int i=0; i< gbm.Length; i++) { + Console.WriteLine($"{i,3} {gbm.Close[i].Value,6:F2} {res1[i].Value,10:F4} {res2[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}"); +} + +#!csharp + +TValSeries test = new(); + +EmaCalc ma1 = new(test, 7, true); +TValSeries res1 = new(ma1); + +EmaCalc ma2 = new(test, 7, false); +TValSeries res2 = new(ma2); + +test.Add(new[]{1.0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0}); + +for (int i=0; i + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + + static double[] CalculateEmaWeights(int period, int barCount) + { + double[] weights = new double[barCount]; + double alpha = 2.0 / (period + 1); + double weightSum = 0; + for (int i = 0; i < barCount; i++) + { + weights[i] = Math.Pow(1 - alpha, i) * alpha; + weightSum += weights[i]; + } + for (int i = 0; i < barCount; i++) + { + weights[i] /= weightSum; + } + return weights; + } + +#!csharp + +Plot plt = new(); +double[] weights = CalculateEmaWeights(10, 30); +var bar = plt.Add.Bars(weights); +plt.Add.Annotation("EMA(10) Weights Chart", Alignment.UpperRight); +var vline = plt.Add.VerticalLine(10.5, width: 1, ScottPlot.Color.FromColor(System.Drawing.Color.Black)); +plt.Display(); + +#!csharp + +#r "..\lib\obj\Debug\QuanTAlib.dll" +using QuanTAlib; +QuanTAlib.Formatters.Initialize(); + +#!csharp + +TSeries Spike = new() { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +TSeries SpikeJMA = new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-0.000552258,-0.000634509,-0.000546757,-0.000418793,-0.000300729,-0.000207311,-0.000138943,-9.12206E-05,-5.89537E-05,-3.76301E-05,-2.3779E-05,-1.49021E-05,-9.27419E-06,-5.73755E-06,-3.53147E-06,-2.16396E-06,-1.32082E-06,-8.03405E-07,-4.87171E-07,-2.94594E-07,-1.77697E-07,-1.06942E-07,-6.42273E-08,-3.85007E-08,-2.3039E-08,-1.37646E-08,-8.21143E-09,-4.89192E-09,-2.91062E-09,-1.72971E-09,-1.02679E-09,-6.08884E-10,0.57216502,0.329278183,0.111882943,0.02038988,-0.00579442,-0.009287974,-0.007184303,-0.004666439,-0.002864099,-0.001904733,-0.001310906,-0.000931811,-0.000671491,-0.000485478,-0.000348532,-0.000247325,-0.00017322,-0.000119743,-8.17596E-05,-5.51943E-05,-3.68781E-05,-2.44116E-05,-1.60241E-05,-1.04391E-05,-6.75421E-06,-4.34304E-06,-2.77694E-06,-1.76649E-06,-1.11846E-06,-7.05115E-07,-4.42777E-07,-2.77032E-07,-1.72747E-07}; +TSeries Impulse = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }; +TSeries ImpulseJMA = new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-0.000552258,-0.000634509,-0.000546757,-0.000418793,-0.000300729,-0.000207311,-0.000138943,-9.12206E-05,-5.89537E-05,-3.76301E-05,-2.3779E-05,-1.49021E-05,-9.27419E-06,-5.73755E-06,-3.53147E-06,-2.16396E-06,-1.32082E-06,-8.03405E-07,-4.87171E-07,-2.94594E-07,-1.77697E-07,-1.06942E-07,-6.42273E-08,-3.85007E-08,-2.3039E-08,-1.37646E-08,-8.21143E-09,-4.89192E-09,-2.91062E-09,-1.72971E-09,-1.02679E-09,-6.08884E-10,0.57216502,0.901443203,1.013326146,1.033716025,1.027921605,1.018633631,1.011449329,1.00678289,1.004181353,1.002789251,1.001927584,1.001373173,1.000992004,1.000715566,1.000511417,1.000361007,1.000251496,1.000172974,1.000117554,1.000079021,1.000052594,1.000034694,1.000022702,1.000014747,1.000009517,1.000006105,1.000003895,1.000002473,1.000001563,1.000000984,1.000000617,1.000000385,1.00000024 }; +TSeries Triangle = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 }; +TSeries TriangleJMA = new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1.581506365,2.477581913,3.488202137,4.521009922,5.548667586,6.567230027,7.578661013,8.58543932,9.589389814,10.59167402,11.59299,12.59374694,13.59418202,14.59443201,15.59457564,16.59465816,17.59470556,18.59473279,19.59474843,20.58051252,21.54811378,22.51426859,23.48461289,24.45940996,25.43756768,26.41800953,27.39996296,28.38293081,29.36660689,30.35080461,31.33541428,32.32037808,33.30566888,33.60143774,33.44671824,32.76693797,31.6743202,30.43080342,29.17128703,27.99351702,26.87387633,25.82071013,24.78859626,23.78848957,22.78804437,21.80499239,20.81281231,19.83220792,18.84031318,17.85875467,16.86608953,15.88343316,14.89022444,13.90693661,12.913562,11.92991131,10.93653629,9.952765982,8.959539985,7.975860495,6.982898571,5.999241471,5.006469162,4.022728123,3.03008006 }; +TSeries Sawtooth = new() { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +TSeries SawtoothJMA = new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1.581506365,2.477581913,3.488202137,4.521009922,5.548667586,6.567230027,7.578661013,8.58543932,9.589389814,10.59167402,11.59299,12.59374694,13.59418202,14.59443201,15.59457564,16.59465816,17.59470556,18.59473279,19.59474843,20.58051252,21.54811378,22.51426859,23.48461289,24.45940996,25.43756768,26.41800953,27.39996296,28.38293081,29.36660689,30.35080461,31.33541428,32.32037808,33.30566888,33.60143774,14.4444661,3.393709459,-0.381095509,-1.087259716,-0.909541115,-0.630797534,-0.431229171,-0.301811285,-0.214698512,-0.155744776,-0.113122186,-0.081470163,-0.057940086,-0.040643203,-0.028127973,-0.019222458,-0.012985805,-0.008681539,-0.005749677,-0.003775885,-0.002460865,-0.001592844,-0.001024611,-0.000655382,-0.00041706,-0.000264159,-0.000166597,-0.000104653,-6.55023E-05,-4.08602E-05,-2.54092E-05}; +TSeries Sine = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 }; +TSeries SineJMA = new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.39,0.490864998,0.636321842,0.775644322,0.887670382,0.96723697,1.002698087,0.994210739,0.97484737,0.899842304,0.760161465,0.598500604,0.417065675,0.226525923,0.028051774,-0.173435555,-0.363836139,-0.541493028,-0.701687888,-0.830525691,-0.924540364,-0.976284568,-1.000434282,-1.002692279,-0.985640624,-0.936128884,-0.822093878,-0.639429302,-0.423527364,-0.195049986,0.026649267,0.230378819,0.416212213,0.587236179,0.726645879,0.840145143,0.922346148,0.974370173,0.998644546,1.001578941,0.980088368,0.929810972,0.819518184,0.64081841,0.420019212,0.180501608,-0.042184191,-0.258138204,-0.444721632,-0.611894672,-0.75049231,-0.857197199,-0.932680738,-0.97935623,-1.000589656,-0.999280528,-0.971643229,-0.914409033,-0.805305412,-0.628446355,-0.408677504,-0.1685546,0.064709114,0.282360193,0.471727962,0.635976367 }; +TSeries Chirp = new() {0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 }; +TSeries ChirpJMA = new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.93,0.561053238,-0.153321154,-0.747445764,-0.827042762,-0.356534975,0.329365258,0.808933304,0.80763454,0.359594859,-0.283946188,-0.780875466,-0.902685225,-0.697018971,-0.08283369,0.516589836,0.888855938,0.948751948,0.779079352,0.219827259,-0.413194221,-0.823676712,-0.966424648,-0.980133795,-0.734382753,-0.187559001,0.381690406,0.771852221,0.950406869,0.992587561,0.906965755,0.568135762,0.057898772,-0.428978529,-0.765170274,-0.939400473,-0.997544642,-0.960715401,-0.780475234,-0.40457484,0.048409781,0.4620447,0.75237274,0.922499475,0.99544436,0.990182899,0.907531892,0.699745667,0.36999061,-0.018042543,-0.374980934,-0.659514743,-0.851442288,-0.960664115,-1.004200191,-0.986072069,-0.905943806,-0.759349136,-0.521563135,-0.211047868,0.115261471,0.410601038,0.645846908,0.817157712,0.92809772,0.989536632 }; +TSeries White = new() { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09}; +TSeries WhiteJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.03,0.000615565,-0.061226573,-0.092671199,-0.129255272,-0.162995702,-0.151470277,-0.092881104,-0.022100823,0.041629282,0.034531011,-0.00690482,-0.057127156,-0.070531037,-0.052561514,-0.063900933,-0.081162364,-0.053975569,-0.003614763,0.007307236,-0.000322726,-0.024840458,-0.018839953,-0.005344501,-0.031085715,-0.032009583,-0.063752546,-0.078008451,-0.076403205,-0.0929075,-0.088024391,-0.087878012,-0.07544228,-0.041364649,-0.00440562,0.005778424,-0.002553758,-0.001239273,0.028362304,0.012498302,-0.007029157,-0.043178553,-0.020123975,-0.061044025,-0.088517916,-0.099905576,-0.102094773,-0.106909564,-0.093572608,-0.097302028,-0.110538244,-0.086147961,-0.087969842,-0.07229411,-0.074396177,-0.054072297,-0.040179437,-0.034162407,-0.050151585,-0.033698183,-0.0108771,-0.011076622,-0.020007675,-0.048169729,-0.073949068,-0.091959026}; +TSeries Gauss = new() { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61}; +TSeries GaussJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001854025,0.011540727,0.011884188,-0.022070199,-0.057369472,-0.051431987,-0.036997357,-0.025664992,-0.032226648,-0.06037637,-0.061423963,-0.036892161,0.008572487,0.058119604,0.108100023,0.133835735,0.129098894,0.093270858,0.04601512,-0.021358793,-0.0867853,-0.129632777,-0.15961389,-0.142828375,-0.123790362,-0.144340705,-0.119583475,-0.02732417,0.022509545,0.093793303,-0.099826639,-0.216160255,-0.274653675,-0.288848025,-0.280078428,-0.227015768,-0.063702909,0.064855103,0.171342223,0.240000551,-0.247868893,-0.178073225,-0.05581201,0.021314181,0.088914096,0.152366403,0.202793617,0.210356779,0.204598892,0.135245478,0.147739125,0.092238129,0.061372665,0.024723623,0.013480088,0.038466805,0.069932732,0.063288258,-0.019191085,-0.097103187,-0.063479437,-0.041277564,0.101208843,0.226564254,0.3324448}; +TSeries B = new() { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06}; +TSeries BJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-0.28,-0.249647418,-0.236137311,-0.092463769,-0.135681216,0.081810351,-0.10737838,0.158877264,-0.121022022,0.157409626,-0.110712469,0.113649408,-0.075459625,0.058705622,-0.03146432,0.018768059,-0.005724484,0.002866003,-0.005347622,-0.002037477,0.001061143,-0.004823112,0.007612129,-0.008318097,0.01176324,-0.023491307,0.038452069,-0.053167834,0.069454301,-0.083773765,0.088390998,-0.093198114,0.083064505,-0.076311968,0.060105968,-0.040755859,0.02672713,-0.017252438,0.003870028,-0.006498957,0.003238924,0.000547304,-0.00071129,0.005308638,-0.005960141,0.008773952,-0.011210224,0.015074455,-0.034416451,0.046128851,-0.058583481,0.078264889,-0.078887801,0.091633873,-0.079308124,0.078776195,-0.057654673,0.048302298,-0.026718666,0.01992987,-0.004897167,0.00466543,-0.006232267,-0.002021568,-0.001167666,-0.00561375}; +TSeries HF = new() { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86}; +TSeries HFJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.009659222,-0.040599553,-0.163792103,-0.267881527,-0.222132422,-0.006792776,0.1652809,0.189468517,-0.003756276,-0.171611112,-0.219673237,-0.03300223,0.141560684,0.221586098,0.063591357,-0.110778668,-0.209827889,-0.090081173,0.08575362,0.199874895,0.119603923,-0.05541179,-0.183498905,-0.142326793,0.03293993,0.17327896,0.162956455,-0.017391711,-0.169867254,-0.189994223,-0.010065626,0.152957221,0.205341138,0.038713249,-0.126568002,-0.205841173,-0.06402948,0.098789546,0.193539028,0.08428847,-0.078530746,-0.186526214,-0.113784853,0.048749086,0.170952283,0.135372811,-0.025218031,-0.158841788,-0.15234029,0.014510823,0.161290048,0.183155809,0.014187729,-0.143632601,-0.196231797,-0.04068555,0.119889198,0.19852797,0.06310687,-0.094480351,-0.18637946,-0.082123757,0.075749245,0.181223306,0.110597239}; +TSeries ImpulseHF = new() { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71}; +TSeries ImpulseHFJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.003461081,-0.011642869,-0.048606972,-0.081865532,-0.066699028,0.004769395,0.061214257,0.066684176,-0.000581688,-0.05790682,-0.073898443,-0.009849831,0.049232603,0.075125066,0.019740445,-0.040036202,-0.073433985,-0.032178662,0.028208365,0.067331942,0.040941342,-0.017234625,-0.059553851,-0.045931589,0.013039637,0.059810689,0.055076438,-0.007043616,-0.058392452,-0.064847235,-0.004085511,0.050829451,0.366220782,0.54546044,0.661280091,0.828257429,1.047815691,1.178437926,1.232617161,1.175409162,1.094265475,1.032680473,1.028996048,1.060829073,1.086121863,1.064452157,1.00462356,0.955209515,0.952483181,1.004256436,1.051127644,1.05790993,1.004105353,0.953026934,0.936162097,0.986603514,1.039284876,1.065119067,1.021791115,0.96988946,0.9395387,0.974261146,1.026993387,1.062164997,1.037299819}; +TSeries SawtoothHF = new() { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3}; +TSeries SawtoothHFJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1.162339653,0.601887587,-0.138147159,1.309223994,5.107713418,8.974394558,10.40295885,9.064227513,7.107532593,7.252214128,10.29253719,14.3422971,16.59550868,15.89741763,13.81397238,12.94610444,15.38646358,19.54492633,22.58742652,23.29047041,21.14259379,19.88472662,20.67288266,24.48774716,28.15328646,29.31043033,28.30880272,26.87074867,26.39868719,29.62694489,33.64590149,35.40176299,35.40262728,32.44992039,16.31419263,6.006475565,2.921574936,2.223213675,0.363348158,-2.56004758,-4.212503588,-4.511484408,-2.01012721,0.279699545,1.556338949,0.733997068,-0.907882416,-1.868190362,-1.078138134,0.727883722,1.831670906,1.372206184,-0.576868493,-1.796676323,-1.608469841,0.271762471,1.547428697,1.743028127,-0.036241215,-1.385714085,-1.83810226,-0.352149236,1.087926207,1.814346637,0.651940618}; +TSeries SineG = new() { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35}; +TSeries SineGJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.135063871,0.399412629,0.568872884,0.655439988,0.727204599,0.985254566,1.112235515,1.150214911,1.056603797,0.850932718,0.730112974,0.654269037,0.593070789,0.475249449,0.324220683,-0.021881615,-0.391582296,-0.739173509,-0.929605151,-1.151983599,-1.258910502,-1.30184982,-1.308701469,-1.196229363,-1.106382636,-1.053626396,-0.828404263,-0.426205314,-0.204837262,0.164514163,0.092688593,0.072220649,0.140038433,0.294703177,0.445530475,0.711949941,1.09235409,1.287255822,1.387026593,1.415102546,0.594414816,0.516509742,0.518120551,0.503063413,0.473652641,0.42622561,0.326965192,-0.029781089,-0.268764145,-0.778292891,-0.896555805,-1.023481762,-1.077021387,-1.109129526,-1.097761437,-1.012370223,-0.917088527,-0.848344828,-0.814846374,-0.770797888,-0.329923163,-0.06907177,0.531362662,0.905121738,1.154870446}; +TSeries ChirpG = new() { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58}; +TSeries ChirpGJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1.3,0.740001439,-0.042556461,-0.769542976,-1.105423545,-0.563312376,0.451052389,0.881976808,0.812897183,0.233094721,-0.529073435,-0.756396598,-0.770130145,-0.466413792,0.150821753,0.776095562,0.987773287,1.009651331,0.657200693,0.082303074,-0.720745569,-1.110291722,-1.190420458,-1.17195634,-0.566303969,-0.176459783,0.062321499,0.809328574,1.253329503,1.191011656,1.114939918,-0.013000588,-0.494064023,-0.797251223,-0.979338687,-1.096367683,-1.088245888,-0.763286688,-0.425472648,0.071930431,0.417858737,0.292028504,0.721092503,1.008767012,1.117325384,1.141306605,1.099161127,0.967706129,0.467009246,0.096168919,-0.606193593,-0.784356282,-0.999208322,-1.061687357,-1.088313979,-1.011248918,-0.770847088,-0.545534111,-0.400816068,-0.317677099,-0.160314675,0.460017228,0.755349431,1.21020452,1.463883777,1.588694006}; +TSeries Complex = new() { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83}; +TSeries ComplexJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175.44,175.6810893,175.8382605,176.4050072,176.1609533,175.974375,175.2236006,175.5533713,174.9884683,174.615941,174.3770309,175.0623389,175.27043,176.32984,175.7164891,176.0973323,175.5682376,175.188465,173.2282697,171.8336671,171.1425322,170.4440719,169.7014159,170.7344496,171.3188789,173.3480163,174.1515576,175.0636931,176.2771412,177.5128437,177.8875485,177.87213,177.3582234,177.4250293,177.357529,177.4802419,177.6010334,177.7295287,177.8216397,177.8136695,175.6183153,174.6439913,174.2091633,173.3115795,172.1448758,171.6820873,171.5763613,172.9091515,174.0986078,175.3366475,175.4742249,175.5553884,174.8667808,174.6161513,173.8022615,172.9494297,172.4487235,172.1450989,171.9957087,171.7495045,171.879957,172.2274366,172.3995248,172.7694942,173.0853957,173.5105324}; +TSeries Market = new() { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25}; +TSeries MarketJMA = new() { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68.75,69.11126386,68.28856883,67.88047045,70.61221899,73.37735131,73.16751764,72.5016599,72.10679156,72.09353157,73.81775589,74.94089624,75.60649969,75.96759618,75.9599046,75.85557397,75.64181903,75.25160642,74.84644659,74.61728297,74.17868676,73.67238488,73.1842394,73.10785283,73.34958901,73.85707168,74.42911902,74.93038851,75.09287507,74.94649321,74.80499937,74.33318217,73.66893412,73.12071155,72.71498835,72.26423085,71.9702028,71.79260915,71.69675123,71.86943138,72.07979919,72.43023876,72.85788116,73.73196015,74.51914619,75.00208707,75.33621901,75.38718804,75.22566212,74.87443544,74.12468013,72.85729881,71.22401395,70.37858241,69.96256092,69.53514922,68.68810853,68.25445186,68.63035835,69.0349436,69.44967111,69.46200796,69.24863466,68.96050991,66.39400988,65.00557185}; + +#!csharp + +public class Jma1 : AbstractBase +{ + public readonly int Period; + private readonly double _phase; + private readonly int _vshort, _vlong; + private CircularBuffer _voltyShort; + private CircularBuffer _vsumBuff; + private CircularBuffer _avoltyBuff; + + private double _beta, _len1, _pow1; + private double _upperBand, _lowerBand, _prevMa1, _prevDet0, _prevDet1, _prevJma; + private double _p_UpperBand, _p_LowerBand, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma; + + public Jma1(int period, double phase = 0, int vshort = 10) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + Period = period; + _vshort = vshort; + _vlong = 65; + _phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5); + + _voltyShort = new CircularBuffer(vshort); + _vsumBuff = new CircularBuffer(_vlong); + _avoltyBuff = new CircularBuffer(2); + + Name = "JMA"; + WarmupPeriod = period * 2; + Init(); + } + + public override void Init() + { + _upperBand = _lowerBand = _prevMa1 = _prevDet0 = _prevDet1 = _prevJma = 0.0; + _p_UpperBand = _p_LowerBand = _p_prevMa1 = _p_prevDet0 = _p_prevDet1 = _p_prevJma = 0.0; + _avoltyBuff.Clear(); + _avoltyBuff.Add(0, true); + _avoltyBuff.Add(0, true); + base.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + // Save current state + _p_UpperBand = _upperBand; + _p_LowerBand = _lowerBand; + _p_prevMa1 = _prevMa1; + _p_prevDet0 = _prevDet0; + _p_prevDet1 = _prevDet1; + _p_prevJma = _prevJma; + } + else + { + // Restore previous state + _upperBand = _p_UpperBand; + _lowerBand = _p_LowerBand; + _prevMa1 = _p_prevMa1; + _prevDet0 = _p_prevDet0; + _prevDet1 = _p_prevDet1; + _prevJma = _p_prevJma; + + } + } + protected override double Calculation() + { + ManageState(Input.IsNew); + + if (_index == 1) + { + _prevMa1 = _prevJma = Input.Value; + return Input.Value; + } + + double del1 = Input.Value - _upperBand; + double del2 = Input.Value - _lowerBand; + double volty = Math.Max(Math.Abs(del1), Math.Abs(del2)); + + _voltyShort.Add(volty, Input.IsNew); + double vsum = _vsumBuff.Newest() + 0.1 * (volty - _voltyShort.Oldest()); + _vsumBuff.Add(vsum, Input.IsNew); + + double avolty = 0; + for (int i = 0; i < _vsumBuff.Count; i++) { avolty += _vsumBuff[i]; } + avolty /= _vsumBuff.Count; + + double rVolty = (avolty > 0) ? volty / avolty *20: 0; + double _len1 = Math.Max((Math.Log(Math.Sqrt(Period)) / Math.Log(2.0)) + 2.0, 0); + double _pow1 = Math.Max(_len1 - 2, 0.5); + + rVolty = Math.Clamp(rVolty, 1.0, Math.Pow(_len1, 1.0 / _pow1)); + + double _pow2 = Math.Pow(rVolty, _pow1); + double _beta = 0.45 * (Period - 1) / (0.45 * (Period - 1) + 2); + double len2 = Math.Sqrt(0.5 * (Period - 1)) * _len1; + + double _Kv = Math.Pow (_beta, Math.Sqrt(_pow2)) *1.5; + + _upperBand = (del1 > 0) ? Input.Value : Input.Value - (_Kv * del1); + _lowerBand = (del2 < 0) ? Input.Value : Input.Value - (_Kv * del2); + + double alpha = Math.Pow(_beta, _pow2); + double ma1 = alpha * (_prevMa1 - Input.Value) + Input.Value; + _prevMa1 = ma1; + + double det0 = _beta * (_prevDet0 - Input.Value + ma1) + Input.Value - ma1; + _prevDet0 = det0; + double ma2 = ma1 + _phase * det0; + + double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - _prevJma)) + (alpha * alpha * _prevDet1 ); + _prevDet1 = det1; + double jma = _prevJma + det1; + _prevJma = jma; + + IsHot = _index >= WarmupPeriod; + return jma; + } +} + +#!csharp + +TSeries ma = Triangle; +TSeries re = TriangleJMA; +TSeries out1 = new(); +TSeries out2 = new(); +Jma calc = new(10); +Jma1 calc1 = new(10); +foreach (var value in ma) { out1.Add(calc.Calc(value)); } +foreach (var value in ma) { out2.Add(calc1.Calc(value)); } +Plot plt = new(); +var sigplot = plt.Add.Signal(ma.v.ToArray()[60..80]); +var jmaplot = plt.Add.Signal(re.v.ToArray()[60..80]); sigplot.Color = ScottPlot.Colors.Red; sigplot.LineWidth = 2; jmaplot.LineWidth = 3; +//var jma1plot = plt.Add.Signal(out1.v.ToArray()[60..80]); jma1plot.Color = ScottPlot.Colors.Purple; jma1plot.LineWidth = 3; +var jma2plot = plt.Add.Signal(out2.v.ToArray()[60..80]); jma2plot.Color = ScottPlot.Colors.Blue; jma2plot.LineWidth = 3; +plt.Display(); + +#!csharp + +#r "nuget: Plotly.net.Interactive" +using Plotly.NET.Interactive; diff --git a/notebooks/means.dib b/notebooks/means.dib new file mode 100644 index 00000000..16530f57 --- /dev/null +++ b/notebooks/means.dib @@ -0,0 +1,24 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\lib\obj\Debug\QuanTAlib.dll" +using QuanTAlib; +QuanTAlib.Formatters.Initialize(); + +#!csharp + +Sma ma1 = new(6); +Gmean ma2 = new (6); +Hmean ma3 = new (6); + +double[] input = new[]{1.0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,12,13,14,15,16,17,18,19,20}; +for (int i=0; i new Candle(DateTime.Now, (decimal)price, (decimal)price, (decimal)price, (decimal)price, 0)).ToList(); + +var ema = new ExponentialMovingAverage(candles, period).Compute().ToList(); + +for (int i=0; i allPoints = new List(); + if (CurrentChart == null) return; + + Graphics gr = args.Graphics; + var mainWindow = CurrentChart.MainWindow; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); + + int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(Time(i)); + int barY = (int)converter.GetChartY(Series![i]); + int halfBarWidth = CurrentChart.BarsWidth / 2; + Point point = new Point(barX + halfBarWidth, barY); + allPoints.Add(point); + } + + if (allPoints.Count > 1) + { + DrawSmoothCombinedCurve(gr, allPoints, this.Count - MovingAverage.WarmupPeriod - rightIndex); + } + } + + private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) + { + if (allPoints.Count < 2) return; + + using (Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) }) + using (Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot }) + { + // Draw the hot part + if (hotCount > 0) + { + var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray(); + gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)Tension); + } + + // Draw the cold part + if (ShowColdValues && hotCount < allPoints.Count) + { + var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray(); + gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)Tension); + } + } + } + + protected void DrawText(Graphics gr, string text, Rectangle clientRect) + { + Font font = new Font("Inter", 8); + SizeF textSize = gr.MeasureString(text, font); + RectangleF textRect = new RectangleF(clientRect.Left + 5, + clientRect.Bottom - textSize.Height - 10, + textSize.Width + 10, textSize.Height + 10); + gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); + gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); + } + + private DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) + { + return lineStyle switch + { + LineStyle.Solid => DashStyle.Solid, + LineStyle.Dash => DashStyle.Dash, + LineStyle.Dot => DashStyle.Dot, + LineStyle.DashDot => DashStyle.DashDot, + _ => DashStyle.Solid, + }; + } +} \ No newline at end of file diff --git a/quantower/Averages/AlmaIndicator.cs b/quantower/Averages/AlmaIndicator.cs new file mode 100644 index 00000000..251ad0f1 --- /dev/null +++ b/quantower/Averages/AlmaIndicator.cs @@ -0,0 +1,28 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class AlmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Offset", sortIndex: 5)] + public double Offset = 0.85; + + [InputParameter("Sigma", sortIndex: 6)] + public double Sigma = 6.0; + private Alma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"ALMA {Period} : {Offset:F2} : {Sigma:F0} : {SourceName}"; + + public AlmaIndicator() : base() + { + Name = "ALMA - Arnaud Legoux Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Alma(period: Period, offset: Offset, sigma: Sigma); + } +} diff --git a/quantower/Averages/Averages.csproj b/quantower/Averages/Averages.csproj new file mode 100644 index 00000000..3fb41cb2 --- /dev/null +++ b/quantower/Averages/Averages.csproj @@ -0,0 +1,30 @@ + + + Indicator + bin\$(Configuration)\ + true + true + true + + + + + + lib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + ..\..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + + + + \ No newline at end of file diff --git a/quantower/Averages/DemaIndicator.cs b/quantower/Averages/DemaIndicator.cs new file mode 100644 index 00000000..185cf6a5 --- /dev/null +++ b/quantower/Averages/DemaIndicator.cs @@ -0,0 +1,22 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class DemaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + private Dema? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"DEMA {Period} : {SourceName}"; + + public DemaIndicator() : base() + { + Name = "DEMA - Double Exponential Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Dema(period: Period); + } +} diff --git a/quantower/Averages/DsmaIndicator.cs b/quantower/Averages/DsmaIndicator.cs new file mode 100644 index 00000000..6e16fd0d --- /dev/null +++ b/quantower/Averages/DsmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class DsmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Dsma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"DSMA {Period} : {SourceName}"; + + public DsmaIndicator() : base() + { + Name = "DSMA - Deviation Scaled Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Dsma(Period); + MinHistoryDepths = ma.WarmupPeriod; + base.InitIndicator(); + } +} diff --git a/quantower/Averages/DwmaIndicator.cs b/quantower/Averages/DwmaIndicator.cs new file mode 100644 index 00000000..5b80c398 --- /dev/null +++ b/quantower/Averages/DwmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class DwmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Dwma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"DWMA {Period} : {SourceName}"; + + + public DwmaIndicator() : base() + { + Name = "DWMA - Double Weighted Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Dwma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/EmaIndicator.cs b/quantower/Averages/EmaIndicator.cs new file mode 100644 index 00000000..7adecd30 --- /dev/null +++ b/quantower/Averages/EmaIndicator.cs @@ -0,0 +1,27 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class EmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Use SMA for warmup", sortIndex: 5)] + public bool UseSma { get; set; } = false; + + private Ema? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"EMA {Period} : {SourceName}"; + + public EmaIndicator() : base() + { + Name = "EMA - Exponential Moving Average"; + Description = "Exponential Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Ema(period: Period, useSma: UseSma); + } +} diff --git a/quantower/Averages/EpmaIndicator.cs b/quantower/Averages/EpmaIndicator.cs new file mode 100644 index 00000000..1a92d33c --- /dev/null +++ b/quantower/Averages/EpmaIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class EpmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Epma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"EPMA {Period} : {SourceName}"; + + public EpmaIndicator() : base() + { + Name = "EPMA - Endpoint Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Epma(period: Period); + } +} diff --git a/quantower/Averages/FramaIndicator.cs b/quantower/Averages/FramaIndicator.cs new file mode 100644 index 00000000..865f4a53 --- /dev/null +++ b/quantower/Averages/FramaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class FramaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Frama? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"FRAMA {Period} : {SourceName}"; + + + public FramaIndicator() : base() + { + Name = "FRAMA - Fractal Adaptive Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Frama(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/FwmaIndicator.cs b/quantower/Averages/FwmaIndicator.cs new file mode 100644 index 00000000..b4a9a389 --- /dev/null +++ b/quantower/Averages/FwmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class FwmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Fwma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"FWMA {Period} : {SourceName}"; + + + public FwmaIndicator() : base() + { + Name = "FWMA - Fibonacci-Weighted Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Fwma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/GmaIndicator.cs b/quantower/Averages/GmaIndicator.cs new file mode 100644 index 00000000..5d85e499 --- /dev/null +++ b/quantower/Averages/GmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +using QuanTAlib; + +public class GmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Gma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"GMA {Period} : {SourceName}"; + + + public GmaIndicator() : base() + { + Name = "GMA - Gaussian-Weighted Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Gma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/HmaIndicator.cs b/quantower/Averages/HmaIndicator.cs new file mode 100644 index 00000000..2dd2b472 --- /dev/null +++ b/quantower/Averages/HmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class HmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Hma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"HMA {Period} : {SourceName}"; + + + public HmaIndicator() : base() + { + Name = "HMA - Hull Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Hma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/HtitIndicator.cs b/quantower/Averages/HtitIndicator.cs new file mode 100644 index 00000000..8d0f4009 --- /dev/null +++ b/quantower/Averages/HtitIndicator.cs @@ -0,0 +1,21 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class HtitIndicator : IndicatorBase +{ + private Htit? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"HTIT : {SourceName}"; + + public HtitIndicator() : base() + { + Name = "HTIT - Hilbert Transform Instantaneous Trendline"; + } + + protected override void InitIndicator() + { + ma = new Htit(); + MinHistoryDepths = ma.WarmupPeriod; + base.InitIndicator(); + } +} diff --git a/quantower/Averages/HwmaIndicator.cs b/quantower/Averages/HwmaIndicator.cs new file mode 100644 index 00000000..e777af3c --- /dev/null +++ b/quantower/Averages/HwmaIndicator.cs @@ -0,0 +1,33 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class HwmaIndicator : IndicatorBase +{ + [InputParameter("nA - smoothed series", sortIndex: 5, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] + public double nA { get; set; } = 0.18; + + [InputParameter("nB - assess the trend (from 0 to 1)", sortIndex: 6, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] + public double nB { get; set; } = 0.1; + + [InputParameter("nC - assess seasonality (from 0 to 1)", sortIndex: 7, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] + public double nC { get; set; } = 0.1; + + private Hwma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"HWMA {nA:F2} : {nB:F2} : {nC:F2} : {SourceName}"; + + + public HwmaIndicator() : base() + { + Name = "HWMA - Holt-Winter Moving Average"; + } + + protected override void InitIndicator() + { + //nA = 2 / (1 + (double)Period); + //nB = 1 / (double)Period; + //nC = 1 / (double)Period; + ma = new Hwma(nA: nA, nB: nB, nC: nC); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/JmaIndicator.cs b/quantower/Averages/JmaIndicator.cs new file mode 100644 index 00000000..88a0a2ea --- /dev/null +++ b/quantower/Averages/JmaIndicator.cs @@ -0,0 +1,26 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class JmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Phase", sortIndex: 2, -100, 100, 1, 0)] + public int Phase { get; set; } = 0; + private Jma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"JMA {Period} : {Phase} : {SourceName}"; + + + public JmaIndicator() : base() + { + Name = "JMA - Jurik Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Jma(period: Period, phase: (double)Phase); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/KamaIndicator.cs b/quantower/Averages/KamaIndicator.cs new file mode 100644 index 00000000..21587ab1 --- /dev/null +++ b/quantower/Averages/KamaIndicator.cs @@ -0,0 +1,28 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class KamaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Fast", sortIndex: 2, 1, 2000, 1, 0)] + public int Fast { get; set; } = 2; + [InputParameter("Slow", sortIndex: 3, 1, 2000, 1, 0)] + public int Slow { get; set; } = 30; + private Kama? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"KAMA {Period} : {Fast} : {Slow} : {SourceName}"; + + + public KamaIndicator() : base() + { + Name = "KAMA - Kaufman's Adaptive Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Kama(Period, Fast, Slow); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/LtmaIndicator.cs b/quantower/Averages/LtmaIndicator.cs new file mode 100644 index 00000000..d786d0bb --- /dev/null +++ b/quantower/Averages/LtmaIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class LtmaIndicator : IndicatorBase +{ + [InputParameter("Gamma", sortIndex: 1, 0, 1, 0.01, 2)] + public double Gamma { get; set; } = 0.10; + + private Ltma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"Laguerre {Gamma:F2} : {SourceName}"; + + public LtmaIndicator() : base() + { + Name = "LTMA - Laguerre Transform Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Ltma(gamma: Gamma); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/MaafIndicator.cs b/quantower/Averages/MaafIndicator.cs new file mode 100644 index 00000000..6cb7128c --- /dev/null +++ b/quantower/Averages/MaafIndicator.cs @@ -0,0 +1,26 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MaafIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 39; + + [InputParameter("Threshold", sortIndex: 5, minimum: 0, maximum: 1, increment: 0.001, decimalPlaces:3)] + public double Threshold = 0.002; + + private Maaf? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"MAAF {Period} : {Threshold:F2} : {SourceName}"; + + public MaafIndicator() : base() + { + Name = "MAAF - Median-Average Adaptive Filter"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Maaf(Period: Period, Threshold: Threshold); + } +} diff --git a/quantower/Averages/MamaIndicator.cs b/quantower/Averages/MamaIndicator.cs new file mode 100644 index 00000000..d3e14f30 --- /dev/null +++ b/quantower/Averages/MamaIndicator.cs @@ -0,0 +1,25 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MamaIndicator : IndicatorBase +{ + [InputParameter("Fast limit", sortIndex: 2, 0, 1, 0.01, 2)] + public double Fast { get; set; } = 0.4; + [InputParameter("Slow limit", sortIndex: 3, 0, 1, 0.01, 2)] + public double Slow { get; set; } = 0.04; + private Mama? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"MAMA : {Fast} : {Slow} : {SourceName}"; + + + public MamaIndicator() : base() + { + Name = "MAMA - MESA Adaptive Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Mama(Fast, Slow); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/MgdiIndicator.cs b/quantower/Averages/MgdiIndicator.cs new file mode 100644 index 00000000..a9ed0bb0 --- /dev/null +++ b/quantower/Averages/MgdiIndicator.cs @@ -0,0 +1,28 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MgdiIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("k Factor", sortIndex: 2, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] + public double kfactor { get; set; } = 0.6; + + + private Mgdi? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"MGDI {Period} : {kfactor:F2} : {SourceName}"; + + + public MgdiIndicator() : base() + { + Name = "MGDI - McGinley Dynamic Index"; + } + + protected override void InitIndicator() + { + ma = new Mgdi(period: Period, kFactor: kfactor); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/MmaIndicator.cs b/quantower/Averages/MmaIndicator.cs new file mode 100644 index 00000000..bf174265 --- /dev/null +++ b/quantower/Averages/MmaIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Mma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"MMA {Period} : {SourceName}"; + + public MmaIndicator() : base() + { + Name = "MMA - Modified Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Mma(period: Period); + } +} diff --git a/quantower/Averages/QemaIndicator.cs b/quantower/Averages/QemaIndicator.cs new file mode 100644 index 00000000..3434249e --- /dev/null +++ b/quantower/Averages/QemaIndicator.cs @@ -0,0 +1,30 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class QemaIndicator : IndicatorBase +{ + [InputParameter("alpha 1", sortIndex: 1, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] + public double k1 { get; set; } = 0.2; + + [InputParameter("alpha 2", sortIndex: 2, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] + public double k2 { get; set; } = 0.3; + [InputParameter("alpha 3", sortIndex: 3, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] + public double k3 { get; set; } = 0.4; + [InputParameter("alpha 4", sortIndex: 4, minimum: 0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] + public double k4 { get; set; } = 0.5; + private Qema? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"QEMA {k1:F2} : {k2:F2} : {k3:F2} : {k4:F2} :{SourceName}"; + + public QemaIndicator() : base() + { + Name = "QEMA - Quad Exponential Moving Average"; + Description = "Quad Exponential Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Qema(k1, k2, k3, k4); + } +} diff --git a/quantower/Averages/RemaIndicator.cs b/quantower/Averages/RemaIndicator.cs new file mode 100644 index 00000000..26193da4 --- /dev/null +++ b/quantower/Averages/RemaIndicator.cs @@ -0,0 +1,26 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class RemaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Regularization Factor", sortIndex: 2, minimum: 0, maximum: 2.5, increment: 0.1, decimalPlaces: 1)] + public double Lambda { get; set; } = 0.5; + + private Rema? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"REMA {Period} : {Lambda:F2} : {SourceName}"; + + public RemaIndicator() : base() + { + Name = "REMA - Regularized Exponential Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Rema(period: Period, lambda: Lambda); + } +} diff --git a/quantower/Averages/RmaIndicator.cs b/quantower/Averages/RmaIndicator.cs new file mode 100644 index 00000000..a89e15a6 --- /dev/null +++ b/quantower/Averages/RmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class RmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Rma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"RMA {Period} : {SourceName}"; + + + public RmaIndicator() : base() + { + Name = "RMA - wildeR Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Rma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/SinemaIndicator.cs b/quantower/Averages/SinemaIndicator.cs new file mode 100644 index 00000000..2f3b231f --- /dev/null +++ b/quantower/Averages/SinemaIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class SinemaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Sinema? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"SINEMA {Period} : {SourceName}"; + + public SinemaIndicator() : base() + { + Name = "SINEMA - Sine-Weighted Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Sinema(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/SmaIndicator.cs b/quantower/Averages/SmaIndicator.cs new file mode 100644 index 00000000..3cbf26fa --- /dev/null +++ b/quantower/Averages/SmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class SmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Sma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"SMA {Period} : {SourceName}"; + + + public SmaIndicator() : base() + { + Name = "SMA - Simple Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Sma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/SmmaIndicator.cs b/quantower/Averages/SmmaIndicator.cs new file mode 100644 index 00000000..a7516e22 --- /dev/null +++ b/quantower/Averages/SmmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class SmmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Smma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"SMMA {Period} : {SourceName}"; + + + public SmmaIndicator() : base() + { + Name = "SMMA - Smoothed Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Smma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/T3Indicator.cs b/quantower/Averages/T3Indicator.cs new file mode 100644 index 00000000..5b8f4766 --- /dev/null +++ b/quantower/Averages/T3Indicator.cs @@ -0,0 +1,29 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class T3Indicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Vfactor", sortIndex: 2, 0, 1, 0.01, 2)] + public double Vfactor { get; set; } = 0.62; + + [InputParameter("Use SMA for warmup", sortIndex: 3)] + public bool UseSma { get; set; } = false; + + private T3? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"T3 {Period} : {Vfactor:F2} : {SourceName}"; + + public T3Indicator() : base() + { + Name = "T3 - Tillson T3 Moving Average"; + } + + protected override void InitIndicator() + { + ma = new T3(period: Period, vfactor: Vfactor, useSma: UseSma); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/TemaIndicator.cs b/quantower/Averages/TemaIndicator.cs new file mode 100644 index 00000000..57d50da3 --- /dev/null +++ b/quantower/Averages/TemaIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class TemaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Tema? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"TEMA {Period} : {SourceName}"; + + public TemaIndicator() : base() + { + Name = "TEMA - Triple Exponential Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Tema(period: Period); + } +} diff --git a/quantower/Averages/TrimaIndicator.cs b/quantower/Averages/TrimaIndicator.cs new file mode 100644 index 00000000..b0612356 --- /dev/null +++ b/quantower/Averages/TrimaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class TrimaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Trima? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"TRIMA {Period} : {SourceName}"; + + + public TrimaIndicator() : base() + { + Name = "TRIMA - Triangular Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Trima(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/VidyaIndicator.cs b/quantower/Averages/VidyaIndicator.cs new file mode 100644 index 00000000..a9ed10ad --- /dev/null +++ b/quantower/Averages/VidyaIndicator.cs @@ -0,0 +1,28 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class VidyaIndicator : IndicatorBase +{ + [InputParameter("Short Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + [InputParameter("Long Period", sortIndex: 2, 1, 2000, 1, 0)] + public int LPeriod { get; set; } = 40; + [InputParameter("Alpha", sortIndex: 3, 0, 1, 0.1, 1)] + public double Alpha { get; set; } = 0.4; + + private Vidya? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"VIDYA {Period} : {SourceName}"; + + + public VidyaIndicator() : base() + { + Name = "VIDYA - Variable Index Dynamic Average"; + } + + protected override void InitIndicator() + { + ma = new Vidya(Period, LPeriod, Alpha); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/WmaIndicator.cs b/quantower/Averages/WmaIndicator.cs new file mode 100644 index 00000000..5cc6a396 --- /dev/null +++ b/quantower/Averages/WmaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class WmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Wma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"WMA {Period} : {SourceName}"; + + + public WmaIndicator() : base() + { + Name = "WMA - Weighted Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Wma(Period); + base.InitIndicator(); + } +} diff --git a/quantower/Averages/ZlemaIndicator.cs b/quantower/Averages/ZlemaIndicator.cs new file mode 100644 index 00000000..c42b045c --- /dev/null +++ b/quantower/Averages/ZlemaIndicator.cs @@ -0,0 +1,24 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class ZlemaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Zlema? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"ZLEMA {Period} : {SourceName}"; + + + public ZlemaIndicator() : base() + { + Name = "ZLEMA - Weighted Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Zlema(Period); + } +} diff --git a/quantower/Averages/_IndicatorBase.cs b/quantower/Averages/_IndicatorBase.cs new file mode 100644 index 00000000..043fd9e1 --- /dev/null +++ b/quantower/Averages/_IndicatorBase.cs @@ -0,0 +1,188 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; +using TradingPlatform.BusinessLayer.Chart; +using System.Runtime.CompilerServices; +using System.Drawing.Drawing2D; +using QuanTAlib; +using System.Collections; +using TradingPlatform.BusinessLayer.TimeSync; + +#pragma warning disable CA1416 // Validate platform compatibility +public abstract class IndicatorBase : Indicator, IWatchlistIndicator +{ + + [InputParameter("Data source", sortIndex: 17, variants: [ + "Open", 1, + "High", 2, + "Low", 3, + "Close", 4, + "HL/2 (Median)", 5, + "OC/2 (Midpoint)", 6, + "OHL/3 (Mean)", 7, + "HLC/3 (Typical)", 8, + "OHLC/4 (Average)", 9, + "HLCC/4 (Weighted)", 10 + ])] + public int Source { get; set; } = 4; + + [InputParameter("Show cold values", sortIndex: 20)] + public bool ShowColdValues { get; set; } = true; + public int MinHistoryDepths; + + // LineSeries.LineSeries(string, Color, int, LineStyle)' + + protected LineSeries? Series; + protected string SourceName; + protected abstract AbstractBase QuanTAlib { get; } + + int IWatchlistIndicator.MinHistoryDepths => 0; + + protected IndicatorBase() : base() + { + OnBackGround = true; + SeparateWindow = false; + SourceName = GetName(Source); + Series = new(name: $"{Name}", color: Color.Yellow, width: 2, style: LineStyle.Solid); + + AddLineSeries(Series); + InitIndicator(); + } + + protected virtual void InitIndicator() + { + SourceName = GetName(Source); + } + + protected override void OnInit() + { + InitIndicator(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar bar = new(Time: Time(), + Open: GetPrice(PriceType.Open), + High: GetPrice(PriceType.High), + Low: GetPrice(PriceType.Low), + Close: GetPrice(PriceType.Close), + Volume: GetPrice(PriceType.Volume), + IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar); + + double price = Source switch + { + 1 => bar.Open, + 2 => bar.High, + 3 => bar.Low, + 4 => bar.Close, + 5 => bar.HL2, + 6 => bar.OC2, + 7 => bar.OHL3, + 8 => bar.HLC3, + 9 => bar.OHLC4, + 10 => bar.HLCC4, + _ => bar.Close + }; + + TValue input = new TValue(bar.Time, price, bar.IsNew); + TValue result = QuanTAlib.Calc(input); + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); + + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + List allPoints = new List(); + if (CurrentChart == null) return; + + Graphics gr = args.Graphics; + var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); + + int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(Time(i)); + int barY = (int)converter.GetChartY(Series![i]); + int halfBarWidth = CurrentChart.BarsWidth / 2; + Point point = new Point(barX + halfBarWidth, barY); + allPoints.Add(point); + } + + if (allPoints.Count > 1) + { + DrawSmoothCombinedCurve(gr, allPoints, this.Count - QuanTAlib.WarmupPeriod - rightIndex); + } + } + + private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) + { + if (allPoints.Count < 2) return; + + using (Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) }) + using (Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot }) + { + // Draw the hot part + if (hotCount > 0) + { + var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray(); + gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)0.2); + } + + // Draw the cold part + if (ShowColdValues && hotCount < allPoints.Count) + { + var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray(); + gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)0.2); + } + } + } + private DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) + { + return lineStyle switch + { + LineStyle.Solid => DashStyle.Solid, + LineStyle.Dash => DashStyle.Dash, + LineStyle.Dot => DashStyle.Dot, + LineStyle.DashDot => DashStyle.DashDot, + _ => DashStyle.Solid, + }; + } + protected void DrawText(Graphics gr, string text, Rectangle clientRect) + { + Font font = new Font("Inter", 8); + SizeF textSize = gr.MeasureString(text, font); + RectangleF textRect = new RectangleF(clientRect.Left + 5, + clientRect.Bottom - textSize.Height - 10, + textSize.Width + 10, textSize.Height + 10); + gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); + gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); + } + protected string GetName(int pType) + { + return pType switch + { + 1 => "Open", + 2 => "High", + 3 => "Low", + 4 => "Close", + 5 => "Median", + 6 => "Midpoint", + 7 => "Mean", + 8 => "Typical", + 9 => "Average", + 10 => "Weighted", + _ => "N/A" + }; + } + +} \ No newline at end of file diff --git a/quantower/Statistics/EntropyIndicator.cs b/quantower/Statistics/EntropyIndicator.cs new file mode 100644 index 00000000..8e141c01 --- /dev/null +++ b/quantower/Statistics/EntropyIndicator.cs @@ -0,0 +1,25 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class EntropyIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] + public int Period { get; set; } = 50; + + private Entropy? entropy; + protected override AbstractBase QuanTAlib => entropy!; + public override string ShortName => $"ENTROPY {Period} : {SourceName}"; + + public EntropyIndicator() : base() + { + Name = "ENTROPY - Entropy"; + SeparateWindow = true; + } + + protected override void InitIndicator() + { + entropy = new(Period); + MinHistoryDepths = entropy.WarmupPeriod; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/KurtosisIndicator.cs b/quantower/Statistics/KurtosisIndicator.cs new file mode 100644 index 00000000..c9d78068 --- /dev/null +++ b/quantower/Statistics/KurtosisIndicator.cs @@ -0,0 +1,25 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class KurtosisIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 4, 2000, 1, 0)] + public int Period { get; set; } = 20; + + private Kurtosis? kurtosis; + protected override AbstractBase QuanTAlib => kurtosis!; + public override string ShortName => $"KURTOSIS {Period} : {SourceName}"; + + public KurtosisIndicator() : base() + { + Name = "KURTOSIS - Relative Flatness"; + SeparateWindow = true; + } + + protected override void InitIndicator() + { + kurtosis = new(Period); + MinHistoryDepths = kurtosis.WarmupPeriod; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/MaxIndicator.cs b/quantower/Statistics/MaxIndicator.cs new file mode 100644 index 00000000..e0b6200a --- /dev/null +++ b/quantower/Statistics/MaxIndicator.cs @@ -0,0 +1,28 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MaxIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 50; + + [InputParameter("Decay to mean", sortIndex: 1, minimum: 0.00, maximum: 100.0, increment: 0.01, decimalPlaces: 2)] + public double Decay { get; set; } = 0.1; + + private Max? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"MAX {Period} : {Decay:F2} : {SourceName}"; + + public MaxIndicator() : base() + { + Name = "MAX - Maximum value (with decay) "; + } + + protected override void InitIndicator() + { + ma = new Max(Period, Decay); + MinHistoryDepths = ma.WarmupPeriod; + Source = 2; + base.InitIndicator(); + } +} diff --git a/quantower/Statistics/MedianIndicator.cs b/quantower/Statistics/MedianIndicator.cs new file mode 100644 index 00000000..e2e29d98 --- /dev/null +++ b/quantower/Statistics/MedianIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MedianIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 50; + + private Median? med; + protected override AbstractBase QuanTAlib => med!; + public override string ShortName => $"MEDIAN {Period} : {SourceName}"; + public MedianIndicator() : base() + { + Name = "MEDIAN - Median historical value"; + } + + protected override void InitIndicator() + { + med = new Median(Period); + MinHistoryDepths = med.WarmupPeriod; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/MinIndicator.cs b/quantower/Statistics/MinIndicator.cs new file mode 100644 index 00000000..a4c14e9e --- /dev/null +++ b/quantower/Statistics/MinIndicator.cs @@ -0,0 +1,27 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class MinIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 50; + + [InputParameter("Decay to mean", sortIndex: 1, minimum: 0.00, maximum: 100.0, increment: 0.01, decimalPlaces: 2)] + public double Decay { get; set; } = 0.1; + + private Min? mi; + protected override AbstractBase QuanTAlib => mi!; + public override string ShortName => $"MIN {Period} : {Decay:F2} : {SourceName}"; + public MinIndicator() : base() + { + Name = "MIN - Minimum value (with decay)"; + } + + protected override void InitIndicator() + { + mi = new Min(Period, Decay); + MinHistoryDepths = mi.WarmupPeriod; + Source = 3; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/ModeIndicator.cs b/quantower/Statistics/ModeIndicator.cs new file mode 100644 index 00000000..c293b643 --- /dev/null +++ b/quantower/Statistics/ModeIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class ModeIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 50; + + private Mode? mode; + protected override AbstractBase QuanTAlib => mode!; + public override string ShortName => $"MODE {Period} : {SourceName}"; + public ModeIndicator() : base() + { + Name = "MODE - Most frequent historical value"; + } + + protected override void InitIndicator() + { + mode = new Mode(Period); + MinHistoryDepths = mode.WarmupPeriod; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/PercentileIndicator.cs b/quantower/Statistics/PercentileIndicator.cs new file mode 100644 index 00000000..dacb2fd9 --- /dev/null +++ b/quantower/Statistics/PercentileIndicator.cs @@ -0,0 +1,28 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; +public class PercentileIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] + public int Period { get; set; } = 20; + + [InputParameter("Percent", sortIndex: 2, 0, 100, 1, 0)] + public double Percent { get; set; } = 50; + + private Percentile? percentile; + protected override AbstractBase QuanTAlib => percentile!; + public override string ShortName => $"PERCENTILE {Period} {Percent:F0}% : {SourceName}"; + + public PercentileIndicator() : base() + { + Name = "PERCENTILE - n-th Percentile "; + SeparateWindow = false; + } + + protected override void InitIndicator() + { + percentile = new(Period, Percent); + MinHistoryDepths = percentile.WarmupPeriod; + base.InitIndicator(); + } + +} \ No newline at end of file diff --git a/quantower/Statistics/SkewIndicator.cs b/quantower/Statistics/SkewIndicator.cs new file mode 100644 index 00000000..7cce283e --- /dev/null +++ b/quantower/Statistics/SkewIndicator.cs @@ -0,0 +1,26 @@ + +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class SkewIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 3, 2000, 1, 0)] + public int Period { get; set; } = 20; + + private Skew? skew; + protected override AbstractBase QuanTAlib => skew!; + public override string ShortName => $"SKEW {Period} : {SourceName}"; + + public SkewIndicator() : base() + { + Name = "SKEW - Skewness"; + SeparateWindow = true; + } + + protected override void InitIndicator() + { + skew = new(Period); + MinHistoryDepths = skew.WarmupPeriod; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/Statistics.csproj b/quantower/Statistics/Statistics.csproj new file mode 100644 index 00000000..36fadba0 --- /dev/null +++ b/quantower/Statistics/Statistics.csproj @@ -0,0 +1,30 @@ + + + Indicator + bin\$(Configuration)\ + true + true + true + + + + + + lib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + ..\..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + + + + \ No newline at end of file diff --git a/quantower/Statistics/StddevIndicator.cs b/quantower/Statistics/StddevIndicator.cs new file mode 100644 index 00000000..6c9648c2 --- /dev/null +++ b/quantower/Statistics/StddevIndicator.cs @@ -0,0 +1,27 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class StddevIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 20; + + [InputParameter("Population", sortIndex: 2)] + public bool IsPopulation { get; set; } = false; + + private Stddev? stddev; + protected override AbstractBase QuanTAlib => stddev!; + public override string ShortName => $"STDDEV {Period} : {SourceName}"; + public StddevIndicator() : base() + { + Name = "STDDEV - Standard Deviation"; + SeparateWindow = true; + } + + protected override void InitIndicator() + { + stddev = new(Period, IsPopulation); + MinHistoryDepths = stddev.WarmupPeriod; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/VarianceIndictor.cs b/quantower/Statistics/VarianceIndictor.cs new file mode 100644 index 00000000..de983873 --- /dev/null +++ b/quantower/Statistics/VarianceIndictor.cs @@ -0,0 +1,28 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class VarianceIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, minimum: 2, maximum: 2000, increment: 1, decimalPlaces: 0)] + public int Period { get; set; } = 20; + + [InputParameter("Population", sortIndex: 2)] + public bool IsPopulation { get; set; } = false; + + private Variance? variance; + protected override AbstractBase QuanTAlib => variance!; + public override string ShortName => $"VAR {Period} : {SourceName}"; + public VarianceIndicator() : base() + { + Name = "VAR - Variance"; + SeparateWindow = true; + } + + protected override void InitIndicator() + { + SeparateWindow = true; + variance = new(Period, IsPopulation); + MinHistoryDepths = variance.WarmupPeriod; + base.InitIndicator(); + } +} \ No newline at end of file diff --git a/quantower/Statistics/ZscoreIndicator.cs b/quantower/Statistics/ZscoreIndicator.cs new file mode 100644 index 00000000..19727511 --- /dev/null +++ b/quantower/Statistics/ZscoreIndicator.cs @@ -0,0 +1,26 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class ZScoreIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] + public int Period { get; set; } = 20; + + private Zscore? zScore; + protected override AbstractBase QuanTAlib => zScore!; + public override string ShortName => $"ZSCORE {Period} : {SourceName}"; + + public ZScoreIndicator() : base() + { + Name = "ZSCORE - Standard Score"; + SeparateWindow = true; + } + + protected override void InitIndicator() + { + zScore = new(Period); + MinHistoryDepths = zScore.WarmupPeriod; + base.InitIndicator(); + } + +} \ No newline at end of file diff --git a/quantower/Statistics/_IndicatorBase.cs b/quantower/Statistics/_IndicatorBase.cs new file mode 100644 index 00000000..aef60342 --- /dev/null +++ b/quantower/Statistics/_IndicatorBase.cs @@ -0,0 +1,190 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; +using TradingPlatform.BusinessLayer.Chart; +using System.Runtime.CompilerServices; +using System.Drawing.Drawing2D; +using System.Collections; +using TradingPlatform.BusinessLayer.TimeSync; + +namespace QuanTAlib; + +#pragma warning disable CA1416 // Validate platform compatibility +public abstract class IndicatorBase : Indicator, IWatchlistIndicator +{ + + [InputParameter("Data source", sortIndex: 17, variants: [ + "Open", 1, + "High", 2, + "Low", 3, + "Close", 4, + "HL/2 (Median)", 5, + "OC/2 (Midpoint)", 6, + "OHL/3 (Mean)", 7, + "HLC/3 (Typical)", 8, + "OHLC/4 (Average)", 9, + "HLCC/4 (Weighted)", 10 + ])] + public int Source { get; set; } = 4; + + [InputParameter("Show cold values", sortIndex: 20)] + public bool ShowColdValues { get; set; } = true; + public int MinHistoryDepths; + + // LineSeries.LineSeries(string, Color, int, LineStyle)' + + protected LineSeries? Series; + protected string SourceName; + protected abstract AbstractBase QuanTAlib { get; } + + int IWatchlistIndicator.MinHistoryDepths => 0; + + protected IndicatorBase() : base() + { + OnBackGround = true; + SeparateWindow = false; + SourceName = GetName(Source); + Series = new(name: $"{Name}", color: Color.RoyalBlue, width: 2, style: LineStyle.Solid); + + AddLineSeries(Series); + InitIndicator(); + } + + protected virtual void InitIndicator() + { + SourceName = GetName(Source); + } + + protected override void OnInit() + { + InitIndicator(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar bar = new(Time: Time(), + Open: GetPrice(PriceType.Open), + High: GetPrice(PriceType.High), + Low: GetPrice(PriceType.Low), + Close: GetPrice(PriceType.Close), + Volume: GetPrice(PriceType.Volume), + IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar); + + double price = Source switch + { + 1 => bar.Open, + 2 => bar.High, + 3 => bar.Low, + 4 => bar.Close, + 5 => bar.HL2, + 6 => bar.OC2, + 7 => bar.OHL3, + 8 => bar.HLC3, + 9 => bar.OHLC4, + 10 => bar.HLCC4, + _ => bar.Close + }; + + TValue input = new TValue(bar.Time, price, bar.IsNew); + TValue result = QuanTAlib.Calc(input); + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); + + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + List allPoints = new List(); + if (CurrentChart == null) return; + + Graphics gr = args.Graphics; + + var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); + + int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(Time(i)); + int barY = (int)converter.GetChartY(Series![i]); + int halfBarWidth = CurrentChart.BarsWidth / 2; + Point point = new Point(barX + halfBarWidth, barY); + allPoints.Add(point); + } + + if (allPoints.Count > 1) + { + DrawSmoothCombinedCurve(gr, allPoints, this.Count - QuanTAlib.WarmupPeriod - rightIndex); + } + } + + private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) + { + if (allPoints.Count < 2) return; + + using (Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) }) + using (Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot }) + { + // Draw the hot part + if (hotCount > 0) + { + var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray(); + gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)0.1); + } + + // Draw the cold part + if (ShowColdValues && hotCount < allPoints.Count) + { + var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray(); + gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)0.1); + } + } + } + private DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) + { + return lineStyle switch + { + LineStyle.Solid => DashStyle.Solid, + LineStyle.Dash => DashStyle.Dash, + LineStyle.Dot => DashStyle.Dot, + LineStyle.DashDot => DashStyle.DashDot, + _ => DashStyle.Solid, + }; + } + protected void DrawText(Graphics gr, string text, Rectangle clientRect) + { + Font font = new Font("Inter", 8); + SizeF textSize = gr.MeasureString(text, font); + RectangleF textRect = new RectangleF(clientRect.Left + 5, + clientRect.Bottom - textSize.Height - 10, + textSize.Width + 10, textSize.Height + 10); + gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); + gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); + } + protected string GetName(int pType) + { + return pType switch + { + 1 => "Open", + 2 => "High", + 3 => "Low", + 4 => "Close", + 5 => "Median", + 6 => "Midpoint", + 7 => "Mean", + 8 => "Typical", + 9 => "Average", + 10 => "Weighted", + _ => "N/A" + }; + } + +} \ No newline at end of file