--- ## Chart for Android/Configuration/Dark and light themes.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; # Configuration of dark and light themes Theme of app can be changed in settings. There are 3 possible themes in settings: Auto, Light, Dark. Auto theme means that library will use system theme. You can define your own colors for dark and light themes of library. All you need is to pass correct implementation of the interface `com.devexperts.dxcharts.lib.ui.theme.DxChartsColors`. How to implement it can be found [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Using-a-theme/Colors-palette) Library provide default colors for light and dark themes. You can get them by using lightPalette() or darkPalette() respectively {` override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val initialTheme = getPalette(settingsRepository.getCurrentTheme()) var theme by remember { mutableStateOf(initialTheme) } setContent{ DxChartsTheme( colors = theme ) { DxChartsScreen( //... themeChanged = { theme = getPalette(it) }, ) } } } fun getPalette(theme: Theme): DxChartsColors { return when (theme) { Theme.AUTO -> { if (isSystemInDarkTheme) darkPalette() else lightPalette() } Theme.LIGHT -> { lightPalette() } Theme.DARK -> { darkPalette() } } } `} As on example DxChartsScreen provide callback for changing theme and it will pass new theme from settings, so you can manage colors accordingly. When we change theme in settings library also trigger changing theme in **`settingsRepository`**, to change colors on chart. Default colors for chart can be found in SettingsRepository implementation that you will use . E.g. if you use our implementation SerializableStorageDefaultSettingsRepository, default colors are storing in defaultColorItemsDark and defaultColorItemsLight. {` val defaultColorItemsDark: Map = mapOf( SettingsScreenItem.BEARISH_BODY to Color(0xffD92C40), SettingsScreenItem.BULLISH_BODY to Color(0xff4D9953), SettingsScreenItem.DOJI to Color(0xffffffff), SettingsScreenItem.BULLISH_BORDER to Color(0xff4D9953), SettingsScreenItem.BEARISH_BORDER to Color(0xffD92C40), SettingsScreenItem.UP to Color(0xff4D9953), SettingsScreenItem.DOWN to Color(0xffD92C40), SettingsScreenItem.NONE to Color(0xffffffff), SettingsScreenItem.AREA to Color(0xFFF44336), SettingsScreenItem.SCATTER to Color(0xffffffff), SettingsScreenItem.VOLUME_BEARISH to Color(0xffD92C40), SettingsScreenItem.VOLUME_BULLISH to Color(0xff4D9953), SettingsScreenItem.VOLUME_DOJI to Color(0xffffffff), SettingsScreenItem.BACKGROUND to Color(0xff000000), SettingsScreenItem.WATERMARK_COLOR to Color(0x1AFFFFFF), SettingsScreenItem.GRID to Color(0xFF282828), SettingsScreenItem.VALUES_ON_SCALES to Color(0xff808080), ) val defaultColorItemsLight: Map = mapOf( SettingsScreenItem.BEARISH_BODY to Color(0xFFEB4C4C), SettingsScreenItem.BULLISH_BODY to Color(0xFF67AD6D), SettingsScreenItem.DOJI to Color(0xff000000), SettingsScreenItem.BULLISH_BORDER to Color(0xFF67AD6D), SettingsScreenItem.BEARISH_BORDER to Color(0xFFEB4C4C), SettingsScreenItem.UP to Color(0xFF67AD6D), SettingsScreenItem.DOWN to Color(0xFFEB4C4C), SettingsScreenItem.NONE to Color(0xff000000), SettingsScreenItem.AREA to Color(0xFFF44336), SettingsScreenItem.SCATTER to Color(0xff000000), SettingsScreenItem.VOLUME_BEARISH to Color(0xFFEB4C4C), SettingsScreenItem.VOLUME_BULLISH to Color(0xFF67AD6D), SettingsScreenItem.VOLUME_DOJI to Color(0xff000000), SettingsScreenItem.BACKGROUND to Color(0xffffffff), SettingsScreenItem.WATERMARK_COLOR to Color(0x1AFFFFFF), SettingsScreenItem.GRID to Color(0xFF282828), SettingsScreenItem.VALUES_ON_SCALES to Color(0xff808080), )`} --- ## Chart for Android/Configuration/Obfuscation rules.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; # Obfuscation rules Obfuscation must be configured with the following rules to ensure proper functionality: {` -keepattributes Signature, *Annotation* -keep class com.devexperts.qd.** { *; } -keep class com.dxfeed.api.impl.** { *; } -keep class com.dxfeed.event.candle.** { *; } -keep class com.dxfeed.event.market.** { *; } -keep class com.google.gson.** { *; } -keep class com.devexperts.dxcharts.lib.domain.** { *; } -keep class com.devexperts.dxcharts.lib.data.repo.** { *; } -keep class com.devexperts.dxcharts.lib.data.model.** { *; } -keep class com.devexperts.dxcharts.provider.** { *; } -dontwarn java.lang.management.ManagementFactory -dontwarn java.rmi.RemoteException -dontwarn javax.management.AttributeNotFoundException -dontwarn javax.management.DynamicMBean -dontwarn javax.management.InstanceAlreadyExistsException -dontwarn javax.management.JMException -dontwarn javax.management.MBeanAttributeInfo -dontwarn javax.management.MBeanConstructorInfo -dontwarn javax.management.MBeanException -dontwarn javax.management.MBeanInfo -dontwarn javax.management.MBeanNotificationInfo -dontwarn javax.management.MBeanOperationInfo -dontwarn javax.management.MBeanParameterInfo -dontwarn javax.management.MBeanServer -dontwarn javax.management.ObjectInstance -dontwarn javax.management.ObjectName -dontwarn javax.management.ReflectionException -dontwarn javax.management.StandardMBean -dontwarn javax.xml.bind.annotation.XmlElement -dontwarn javax.xml.bind.annotation.XmlNsForm -dontwarn javax.xml.bind.annotation.XmlRootElement -dontwarn javax.xml.bind.annotation.XmlSchema -dontwarn javax.xml.bind.annotation.XmlSchemaType -dontwarn javax.xml.bind.annotation.XmlTransient -dontwarn javax.xml.bind.annotation.XmlType -dontwarn javax.xml.bind.annotation.adapters.XmlAdapter -dontwarn javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter -dontwarn javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters -dontwarn kotlinx.datetime.Clock$System -dontwarn kotlinx.datetime.Instant -dontwarn org.apache.log4j.Appender -dontwarn org.apache.log4j.Category -dontwarn org.apache.log4j.ConsoleAppender -dontwarn org.apache.log4j.Layout -dontwarn org.apache.log4j.Priority -dontwarn org.apache.log4j.RollingFileAppender -dontwarn org.apache.log4j.spi.LoggingEvent -dontwarn org.apache.logging.log4j.Level -dontwarn org.apache.logging.log4j.LogManager -dontwarn org.apache.logging.log4j.Logger -dontwarn org.apache.logging.log4j.Marker -dontwarn org.apache.logging.log4j.core.Appender -dontwarn org.apache.logging.log4j.core.Filter$Result -dontwarn org.apache.logging.log4j.core.Filter -dontwarn org.apache.logging.log4j.core.Layout -dontwarn org.apache.logging.log4j.core.LogEvent -dontwarn org.apache.logging.log4j.core.Logger -dontwarn org.apache.logging.log4j.core.LoggerContext -dontwarn org.apache.logging.log4j.core.appender.AbstractAppender$Builder -dontwarn org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender$Builder -dontwarn org.apache.logging.log4j.core.appender.ConsoleAppender$Builder -dontwarn org.apache.logging.log4j.core.appender.ConsoleAppender$Target -dontwarn org.apache.logging.log4j.core.appender.ConsoleAppender -dontwarn org.apache.logging.log4j.core.appender.RollingFileAppender$Builder -dontwarn org.apache.logging.log4j.core.appender.RollingFileAppender -dontwarn org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy -dontwarn org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy -dontwarn org.apache.logging.log4j.core.config.Configuration -dontwarn org.apache.logging.log4j.core.config.ConfigurationSource -dontwarn org.apache.logging.log4j.core.config.DefaultConfiguration -dontwarn org.apache.logging.log4j.core.config.LoggerConfig -dontwarn org.apache.logging.log4j.core.config.NullConfiguration -dontwarn org.apache.logging.log4j.core.config.plugins.Plugin -dontwarn org.apache.logging.log4j.core.config.plugins.PluginConfiguration -dontwarn org.apache.logging.log4j.core.config.plugins.PluginFactory -dontwarn org.apache.logging.log4j.core.filter.ThresholdFilter -dontwarn org.apache.logging.log4j.core.layout.AbstractStringLayout$Serializer -dontwarn org.apache.logging.log4j.core.layout.AbstractStringLayout -dontwarn org.apache.logging.log4j.core.pattern.MessagePatternConverter -dontwarn org.apache.logging.log4j.message.Message -dontwarn org.apache.logging.log4j.message.SimpleMessage -dontwarn org.apache.logging.log4j.spi.LoggerContext -dontwarn org.apache.logging.log4j.status.StatusLogger`} --- ## Chart for Android/Configuration/Overview.md # DxCharts Android Library Configuration In the DxCharts Android library, you can configure: 1. [String resources by overriding the *strings.xml* within the application](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Strings). 2. [UI by modifying parameters of the `com.devexperts.dxcharts.lib.ui.theme.DxChartsTheme` object](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Overview). 3. Library functionality by changing parameters ([repositories](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Repositories/Overview), [providers](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Providers/Overview) or initial instrument) of the `com.devexperts.dxcharts.lib.ui.theme.DxChartsConfig` object. --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/Candles Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Candles Provider Provider that supplies data about candles for the selected trading instrument. The chart listens to **`candlesFlow`**, a flow of candle data updates, and reacts to changes in it by updating the displayed information accordingly. Whenever there is a new candle data emitted by **`candlesFlow`**, the chart updates to reflect this new data, ensuring that the visual representation of the trading instrument's price movements is always current and accurate. {` /** * Interface for receiving candles data * * When loading dxCharts library chart, data about candles is taken from this interface * * When connecting dxCharts library, developer can implement this interface, or use the default implementation [com.devexperts.dxcharts.provider.candles.DxFeedCandlesProvider] and pass it to the library using [DxChartsDataProviders] data class * * The package *com.devexperts.dxcharts.provider.candles:dxcharts_dxfeed_candles_provider* contains an implementation of the interface for working with dxFeed * * Use [candlesFlow] to get candles data * * Use [isDataAvailable] to get state of candles data. When it is false, we show corresponding message on chart and block everything except changing symbol * * Use [isLoading] to get state if candles are loading. When it is true, we show pulsating animation on chart and block interactions with it * * Use [changeParams] to change candles params */ interface DxChartsCandlesProvider { /** * Flow of receiving candles data * * Candles data is represented by [DxChartsCandles] */ val candlesFlow: StateFlow /** * Flow of receiving state of candlesData */ val isDataAvailable: StateFlow /** * Flow of receiving state if candles data is loading after params changes */ val isLoading: StateFlow /** * Changes candles params for dxFeed * * @param symbol instrument symbol * @param priceType price type of the instrument (bid, ask, last, market) * @param aggregation aggregation of the instrument * @param extendedHours extended hours flag * @param alignSessionStart align session start flag */ fun changeParams( symbol: String, priceType: PriceType, aggregation: Aggregation, extendedHours: Boolean, alignSessionStart: Boolean ) } `} ### Method: changeParams This method is used to change the parameters for the candles data. It takes the following parameters: - `symbol`: The symbol of the selected instrument. A symbol is a unique identifier used for trading on the exchange. For example, the symbol for Apple stocks on NASDAQ is "AAPL". - `priceType`: The price type of the instrument. It can be "bid" (the price at which traders are willing to buy the asset), "ask" (the price at which traders are willing to sell the asset), "last" (the price of the last completed transaction), or "market" (the current market price). - `aggregation`: The aggregation of the instrument, the minimum time interval between candles. For example, if the aggregation is 1 minute, each candle on the chart will represent 1 minute of trading. - `extendedHours`: A flag indicating the need to obtain candles outside of trading hours. If the flag is set to `true`, the data will include information about trading before and after the main trading time. - `alignSessionStart`: A flag indicating the need to align candles with the start of the trading session. If the flag is set to `true`, the start of each candle will coincide with the start of the trading session. #### Example Here is an example of how to use the `changeParams` method: {` candlesProvider.changeParams( symbol = "AAPL", priceType = PriceType.LAST, aggregation = Aggregation(1, Aggregation.TimeUnit.MINUTES), extendedHours = true, alignSessionStart = false ) `} Data is sent by updating state of the `candlesFlow` variable. Class `com.devexperts.dxcharts.provider.domain.DxChartsCandles` - a complete array of candles for the chart, along with their properties: {` /** * Data class for storing chart candles data and general information about its properties. * * @property candlesData List of [CandleDO] objects representing the candles data. * @property aggregation [Aggregation] of the candles data. * @property symbol Symbol of the instrument for which the candles data is provided. * @property extendedHours Flag indicating whether the candles data includes extended hours (before and after market hours). * @property alignedToSession Flag indicating whether the candles data is aligned to the session. If true, the candles data is aligned to the session. If false, the candles data is aligned to the beginning of the day. * @property priceType [PriceType] of the candles data. Can be [PriceType.LAST] (price of the last completed transaction), [PriceType.BID] (price at which traders are willing to buy the asset), [PriceType.ASK] (price at which traders are willing to sell the asset), or [PriceType.MARKET] (current market price). */ data class DxChartsCandles( val candlesData: List = emptyList(), val aggregation: Aggregation = Aggregation.HOUR, val symbol: String = "", val extendedHours: Boolean = false, val alignedToSession: Boolean = false, val priceType: PriceType = PriceType.LAST ) /** * Data class representing a single candle data object. * * @property high High price of the candle. * @property low Low price of the candle. * @property open Opening price of the candle. * @property close Closing price of the candle. * @property timestamp Timestamp of the candle in milliseconds. * @property volume Volume of sales for the candle. * @property expansion Optional expansion flag. * @property id Optional candle id. * @property impVolatility Optional implied volatility. * @property vWap Optional volume weighted average price. */ data class CandleDO( val high: Double, val low: Double, val open: Double, val close: Double, val timestamp: Long, val volume: Long, val expansion: Boolean? = null, val id: Int? = null, val impVolatility: Double? = null, val vWap: Double? = null ) /** * Data class for chart's aggregation data. * * Aggregation represents the minimum time interval between candles. * * @property value Value of the aggregation, representing the number of time units. * @property multiplier [TimeUnit] multiplier of the aggregation. Can be [TimeUnit.SECONDS], [TimeUnit.MINUTES], etc. */ data class Aggregation( val value: Int, val multiplier: TimeUnit = TimeUnit.SECONDS, ) { /** * Enum class for storing time unit * * Values: [SECONDS], [MINUTES], [HOURS], [DAYS], [WEEKS], [MONTHS], [YEARS] */ enum class TimeUnit(val value: String, val letter: String) { SECONDS("SECONDS", "s"), MINUTES("MINUTES", "m"), HOURS("HOURS", "h"), DAYS("DAYS", "d"), WEEKS("WEEKS", "w"), MONTHS("MONTHS", "M"), YEARS("YEARS", "y"); } } `} Candles set on the chart look as follows: ![candles_on_chart](../../../../images/candles_on_chart.png) Here is the default implementation of **`DxChartsCandlesProvider`**: {` /** * dxCharts library Candles provider for fetching candles data using the dxFeed library. * * The "com.devexperts.qd:qds" library is used to connect to the dxFeed API. * * The process of obtaining candles is performed in a separate thread [executorService] using [endpoint] from the [connect] method: * - Data is retrieved from [dataFlow] by subscribing to it. The data is sent as [DxChartsCandles]. * * To start the provider, you need to call the [connect] method, which: * - Connects to the dxFeed API at [endpointAddress]. * - Connects [feed] - the object for working with the dxFeed API, to [candlesModel] - the dxFeed API model that receives and processes candles data. * - Connects [listener], which specifies the algorithm for processing and delivering data to dxCharts lib, to the [candlesModel] model. * - Launches the dxFeed API within a separate thread with [executorService]. * * When calling the [connect] method, no parameters for obtaining candles will be passed to the dxFeed API, so no data will be received. * * Use the [changeParams] method to pass the necessary parameters for operations. This includes setting the symbol, price type, aggregation period, and other parameters. * * The minimum time interval between candle transmissions to dxCharts lib is [CANDLES_DATA_UPDATE_DELAY]. * * Candle transmission occurs through [dataFlow], which holds the current state of candles and is subscribed to within the dxCharts library. * * To terminate the provider, call the [disconnect] method. After calling this method: * - [listener] is removed from the [candlesModel]. * - [endpoint] is disconnected from the dxFeed API. * - [feed] is disconnected from [candlesModel]. * - [executorService] finishes its work and clears the thread. * * Errors occurring during the disconnection process will be logged and emitted through [errorFlow]. * * @property endpoint Object for connecting to the dxFeed API. * @property feed Object for working with the dxFeed API. * @property executorService Service providing a separate thread for working with the dxFeed API. * @property candlesModel dxFeed API model that receives and processes candle data. * @property _dataFlow Private flow for transmitting candle data to dxCharts lib. * @property dataFlow External flow for transmitting candle data to dxCharts lib. * @property currentCandlesData Current parameters for obtaining candles. * @property lastEmitTime Time of the last data transmission to dxCharts lib. * @property listener Algorithm for processing and delivering data from dxFeed API to dxCharts lib. * @property _isDataAvailable Represents the availability of data as a StateFlow. It is initially set to 'true'. * @property isDataAvailable Provides a StateFlow to observe the availability of data. * @property _isLoading Private flow for the state if we are in the process of loading candles data. * @property isLoading Provides a StateFlow to observe the loading state. * @property job Job instance for managing the coroutine responsible for checking data availability. * @property _errorFlow Internal [MutableStateFlow] for sending errors. * @property errorFlow [StateFlow] for sending errors. */ class DxFeedCandlesProvider( private val endpointAddress: String ) : DxChartsCandlesProvider, DxChartsErrorProvider { private val endpoint = DXEndpoint.getInstance() private val feed: DXFeed = endpoint.feed private val executorService = Executors.newFixedThreadPool(1) private val candlesModel = TimeSeriesEventModel(Candle::class.java) private val _dataFlow: MutableStateFlow = MutableStateFlow(DxChartsCandles()) override val dataFlow: StateFlow get() = _dataFlow private val _errorFlow: MutableStateFlow = MutableStateFlow(null) override val errorFlow: StateFlow get() = _errorFlow private var currentCandlesData: DxChartsCandles = DxChartsCandles() private var lastEmitTime = 0L private val _isDataAvailable: MutableStateFlow = MutableStateFlow(true) override val isDataAvailable: StateFlow get() = _isDataAvailable private val _isLoading: MutableStateFlow = MutableStateFlow(true) override val isLoading: StateFlow get() = _isLoading private var job: Job? = null /** * Algorithm for processing and delivering data from dxFeed API to dxCharts lib * * Upon receiving new candle data, if more than [CANDLES_DATA_UPDATE_DELAY] milliseconds have passed since the last data transmission to dxCharts lib, * the data is transmitted to dxCharts lib using [dataFlow]. * * Candles received from dxFeed API [com.dxfeed.model.IndexedEventModel] are transformed into [CandleDO] using the [toDataObject] method. */ private val listener = ObservableListModelListener { if (System.currentTimeMillis() - CANDLES_DATA_UPDATE_DELAY >= lastEmitTime) { val candlesData = candlesModel.eventsList.toDataObject() startDataAvailabilityObserver(candlesData) } } private fun startDataAvailabilityObserver(candlesData: List) { if (job != null) { job?.cancel() job = null } job = CoroutineScope(Dispatchers.IO).launch { try { if (candlesData.isEmpty()) { _isLoading.tryEmit(true) delay(DATA_AVAILABILITY_CHECK_DELAY) if (candlesModel.eventsList.isEmpty()) { updateChartData(candlesData, false) } } else { updateChartData(candlesData, true) } } catch (e: CancellationException){ Log.d("DxChartsProviderError", "Coroutine was cancelled") } catch (e: Exception) { _errorFlow.tryEmit(CandlesProviderError.UnknownError("Unknown error while observing candles availability: $e.message")) } } } /** * Connection to the dxFeed API * - Connects [endpoint] to the dxFeed API at [endpointAddress]. * - Connects [feed] to [candlesModel]. * - Connects [listener] to [candlesModel]. * - Launches [endpoint] within a separate thread [executorService]. */ fun connect() { try { _errorFlow.tryEmit(null) endpoint.connect(endpointAddress) candlesModel.attach(feed) candlesModel.eventsList.addListener(listener) startDataAvailabilityObserver(emptyList()) executorService.execute { try { endpoint.awaitNotConnected() } catch (e: Exception) { _errorFlow.tryEmit(CandlesProviderError.NetworkError("Connection to candle provider was interrupted: $e.message", e)) } } } catch (e: Exception) { _errorFlow.tryEmit(CandlesProviderError.UnknownError("Unknown error during connecting to candle provider: $e.message", e)) } } /** * Changing parameters for obtaining candles * * - Sets the instrument name [CandleSymbol], as well as the price type [CandlePrice] and candle period [CandlePeriod]. * - Sets flags [CandleSession] and [CandleAlignment] based on the passed parameters. * - Sets the current parameters for obtaining candles in [currentCandlesData]. * * @param symbol Instrument name for which to obtain candles * @param priceType Price type by which to obtain candles * @param aggregation Time interval between candles on the chart * @param extendedHours Flag indicating the need to obtain candles outside of trading hours * @param alignSessionStart Flag indicating the need to align candles with the start of the trading session */ @Synchronized override fun changeParams( symbol: String, priceType: com.devexperts.dxcharts.provider.domain.PriceType, aggregation: Aggregation, extendedHours: Boolean, alignSessionStart: Boolean ) { try { candlesModel.fromTime = START_TIME candlesModel.symbol = CandleSymbol.valueOf( symbol, when (priceType) { com.devexperts.dxcharts.provider.domain.PriceType.BID -> CandlePrice.BID com.devexperts.dxcharts.provider.domain.PriceType.ASK -> CandlePrice.ASK com.devexperts.dxcharts.provider.domain.PriceType.LAST -> CandlePrice.LAST com.devexperts.dxcharts.provider.domain.PriceType.MARKET -> CandlePrice.MARK }, aggregation.toCandlePeriod(), if (extendedHours || !aggregation.isTHOEnabled()) CandleSession.ANY else CandleSession.REGULAR, if (alignSessionStart) CandleAlignment.SESSION else CandleAlignment.MIDNIGHT ) currentCandlesData = DxChartsCandles( symbol = symbol, aggregation = aggregation, extendedHours = extendedHours, alignedToSession = alignSessionStart, priceType = priceType ) } catch (e: IllegalArgumentException) { _errorFlow.tryEmit(CandlesProviderError.InvalidSymbol("Invalid symbol: $symbol", e)) } catch (e: Exception) { _errorFlow.tryEmit(CandlesProviderError.UnknownError("Unknown error after changing parameters for candle provider: $e.message", e)) } } /** * Updates the chart data with the provided list of candles and data availability status. * * @param candlesData The list of candles to update the chart data with. * @param isDataAvailable The status indicating whether data is available. */ private suspend fun updateChartData(candlesData: List, isDataAvailable: Boolean) { _dataFlow.tryEmit( currentCandlesData.copy( candlesData = candlesData ) ).apply { Log.d(TAG, "candles loaded") } _isDataAvailable.tryEmit(isDataAvailable) lastEmitTime = System.currentTimeMillis() delay(500) _isLoading.tryEmit(false) } private fun Aggregation.isTHOEnabled(): Boolean{ return when (this.multiplier){ TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS -> true else -> false } } /** * Disconnect from the dxFeed API * - [job] canceled and changed to null * - [listener] is removed from the [candlesModel]. * - [endpoint] is disconnected from the dxFeed API. * - [feed] is disconnected from [candlesModel]. * - [executorService] finishes its work and clears the thread. * - The instrument name within [candlesModel] is set to an empty string. */ fun disconnect() { try { job?.cancel() job = null candlesModel.eventsList.removeListener(listener) endpoint.disconnectAndClear() candlesModel.detach(feed) executorService.shutdown() candlesModel.symbol = "" } catch (e: Exception) { _errorFlow.tryEmit(CandlesProviderError.UnknownError("Unknown error during disconnection of candle provider $e.message")) } } /** * @value ENDPOINT_ADDRESS URL address of the dxFeed API * @value CANDLES_DATA_UPDATE_DELAY minimum time interval between candle transmissions to dxCharts lib * @value DATA_AVAILABILITY_CHECK_DELAY time interval for understanding that we have no candles data * @value START_TIME the start timestamp in milliseconds from which candle data is provided */ companion object { const val TAG = "DxFeedCandlesProvider" const val CANDLES_DATA_UPDATE_DELAY = 200L const val DATA_AVAILABILITY_CHECK_DELAY = 14500L const val START_TIME = 0L } } /** * Conversion of [Aggregation] to [CandlePeriod] */ private fun Aggregation.toCandlePeriod(): CandlePeriod { val candleType = when (this.multiplier) { TimeUnit.SECONDS -> CandleType.SECOND TimeUnit.MINUTES -> CandleType.MINUTE TimeUnit.HOURS -> CandleType.HOUR TimeUnit.DAYS -> CandleType.DAY TimeUnit.WEEKS -> CandleType.WEEK TimeUnit.MONTHS -> CandleType.MONTH TimeUnit.YEARS -> CandleType.YEAR } return CandlePeriod.valueOf(this.value.toDouble(), candleType) } /** * Conversion of [List] of [Candle] to [List] of [CandleDO] */ private fun List.toDataObject() = this.map { it.toDataObject() } /** * Conversion of [Candle] to [CandleDO] */ private fun Candle.toDataObject(): CandleDO = CandleDO( high = this.high, low = this.low, open = this.open, close = this.close, timestamp = this.time, volume = this.volume, expansion = false, id = null, impVolatility = impVolatility, vWap = if (vwap.isNaN()) null else vwap ) /** * Sealed class for representing different types of errors in the DxFeedCandlesProvider. */ sealed class CandlesProviderError(override val message: String, override val error: Throwable?) : ProviderError { data class NetworkError( override val message: String, override val error: Throwable? = null ) : CandlesProviderError(message, error) data class InvalidSymbol( override val message: String, override val error: Throwable? = null ) : CandlesProviderError(message, error) data class UnknownError( override val message: String, override val error: Throwable? = null ) : CandlesProviderError(message, error) } `} --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/Events Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Events Provider Provider that supplies data about events for the selected trading instrument - corporate actions, corporate calendar, earning reports. {` /** * Interface for receiving events data * * Events: dividends, splits, earnings, conference calls * * After loading dxCharts library chart, events data is taken from this interface * * When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.events.DxFeedEventsProvider] and pass it to the library using [DxChartsDataProviders] data class * * * Use [dataFlow] to get events data * * Use [changeSymbol] to change events' instrument symbol */ interface DxChartsEventsProvider { /** * Flow of receiving events data * * Events data is represented by [Events] - [Events.dividends], [Events.splits], [Events.earnings], [Events.conferenceCalls] */ val dataFlow: StateFlow /** * Changes events' instrument symbol * * @param symbol instrument symbol */ fun changeSymbol(symbol: String) } `} ### Method: changeSymbol This method is used to change the symbol of the instrument for which the events data is provided. It takes the following parameter: - `symbol`: The symbol of the selected instrument. A symbol is a unique identifier used for trading on the exchange. For example, the symbol for Apple stocks on NASDAQ is "AAPL". #### Example Here is an example of how to use the `changeSymbol` method: {` eventsProvider.changeSymbol("AAPL") `} Data is sent by updating the state of the `dataFlow` variable. Class `com.devexperts.dxcharts.provider.domain.Events` - an object storing arrays of all events on the chart: {` /** * Data class for storing data about events (dividends, earnings, splits, conference calls) * * @property dividends List of [DividendsEvent] objects representing the dividends events. * @property earnings List of [EarningsEvent] objects representing the earnings events. * @property splits List of [SplitsEvent] objects representing the splits events. * @property conferenceCalls List of [ConferenceCallsEvent] objects representing the conference calls events. */ data class Events( val dividends: List, val earnings: List, val splits: List, val conferenceCalls: List ) { /** * Data class for storing data about a dividends event. * * @property gross Gross value of the dividends. * @property timestamp Timestamp of the dividends event in milliseconds. */ data class DividendsEvent( val gross: Float, val timestamp: Long ) /** * Data class for storing data about an earnings event. * * @property basic Basic value of earnings (e.g., 4.19). * @property diluted Diluted value of earnings (e.g., 4.16). * @property timestamp Timestamp of the earnings event in milliseconds. * @property periodEnding Timestamp in milliseconds of the period ending (e.g., Sep 2023). */ data class EarningsEvent( val basic: Float, val diluted: Float, val timestamp: Long, val periodEnding: Long ) /** * Data class for storing data about a splits event. * * @property splitFrom "Split from" value. * @property splitTo "Split to" value. * @property timestamp Timestamp of the splits event in milliseconds. */ data class SplitsEvent( val splitFrom: Float, val splitTo: Float, val timestamp: Long ) /** * Data class for storing data about a conference calls event. * * @property timestamp Timestamp of the conference calls event in milliseconds. * @property referencePeriod Reference period of the conference calls event. This is a string that usually represents a quarter and year (e.g., "Q3 2021"), but the format may vary depending on the data source. */ data class ConferenceCallsEvent( val timestamp: Long, val referencePeriod: String, ) } `} Events set on the chart look as follows: ![events_on_chart](/android-images/images/events_on_chart.png) Here is the default implementation of **`DxChartsEventsProvider`**: {` /** * Default implementation of [DxChartsEventsProvider] * * Uses dxFeed API to get events data * * Uses the DxFeed api via Http requests using [OkHttpClient] * * Path to get dividends data: corporate-action/symbol/ [CORPORATE_ACTIONS] * * Path to get events data: corporate-action/symbol/ [CORPORATE_ACTIONS] * * Path to get conference calls data: corporate-calendar/symbol/ [EARNING] * * Path to get earnings data: earning-report/symbol/ [EARNING] * * A token [token] is used for authorization with Base64 encoding * * When loading dxCharts library and starting the provider ([changeSymbol]), a request is sent with the current symbol of the instrument ([symbol]) * with 2 paths - [CORPORATE_ACTIONS], [EARNING] using [requestCorporateActions] and [requestEarnings] methods * * Query responses results are put into objects [EventsResponse]. * * Then the data is parsed and divided into objects [DxFeedCorporateAction] and [DxFeedEarning]. * * The next step is the conversion and normalization of data into data classes used by the library [Events.SplitsEvent], [Events.DividendsEvent], [Events.EarningsEvent], [Events.ConferenceCallsEvent] with extensions [convertAndNormalizeSplits], [convertAndNormalizeDividends], [convertAndNormalizeEarnings], [convertAndNormalizeCalls] * * After this, the data is transferred to [Events] object and sent to [dataFlow] * * Requests occurs once every 10 seconds ([UPDATE_DELAY]). * If an error occurs during the request, it is repeated every 500 milliseconds ([REQUEST_DELAY]) * * The data retrieval logic is executed in a separate thread using [ExecutorService] [executorService]. * * Inside a separate thread [requester] method id executed - method that executes queries and processes responses. * * Changing the symbol of the instrument and starting the work of the provider occurs using [changeSymbol] method - if the provider has already been started, then the thread with it stops, a new thread with a new symbol is started * * To manually stop the provider, you can use [disconnect] method. It stops the provider, cleans [executorService] and disconnects from dxFeed API. * * @property dataFlow flow of received and processed events data * @property gson Gson object for parsing json * @property executorService executor service with for running requests in separate thread (creating with Executors.newFixedThreadPool(1)) * @property _dataFlow internal [MutableStateFlow] of received and processed events data * @property dataFlow public [StateFlow] of received and processed events data that is getting state from [_dataFlow] * @property client [OkHttpClient] for sending requests * @property request [Request] for sending requests with "Authorization" header with [token] * @property loaded flag that shows if data was successfully loaded in [requester] method * @property connected flag that shows if provider is working and connected to dxFeed API * @property symbol current symbol of selected instrument * @property _errorFlow Internal [MutableStateFlow] for sending errors. * @property errorFlow [StateFlow] for sending errors. */ class DxFeedEventsProvider( private val url: String = "", private val token: String = "", ) : DxChartsEventsProvider, DxChartsErrorProvider { override val dataFlow: StateFlow get() = _dataFlow private val gson = Gson() private var executorService: ExecutorService? = null private val _dataFlow = MutableStateFlow(Events.EMPTY) private val _errorFlow: MutableStateFlow = MutableStateFlow(null) override val errorFlow: StateFlow get() = _errorFlow private val client = OkHttpClient() .newBuilder() .build() private val request = Request.Builder() .addHeader("Authorization", token) @Volatile private var loaded = false @Volatile private var connected = false @Volatile private var symbol: String? = null /** * Changes events' instrument symbol and starts/restarts provider * If provider is already running, then it will be stopped and new thread with new symbol will be started * * [requester] method will be called with new symbol in new thread * * Initial events data after starting/restarting provider will be empty [Events.EMPTY] * * [connected] flag will be set to true * * @param symbol instrument symbol */ override fun changeSymbol(symbol: String) { executorService?.let { if (!it.isShutdown) { try { it.shutdownNow() } catch (e: Exception) { _errorFlow.tryEmit(EventsProviderError.UnknownError("Failed to shutdown executor service: $e.message", e)) } } } this.symbol = symbol connected = true _dataFlow.tryEmit(Events.EMPTY) executorService = Executors.newFixedThreadPool(1) executorService?.execute { requester() } } /** * Stops provider, cleans [executorService] and disconnects from dxFeed API * * [connected] flag will be set to false * * [loaded] flag will be set to true * * [executorService] will be shutdown */ fun disconnect() { connected = false loaded = true try { executorService?.shutdownNow() } catch (e: Exception) { _errorFlow.tryEmit(EventsProviderError.UnknownError("Failed to shutdown executor service: $e.message", e)) } } /** * Method for a looped requesting and processing ([requestEvents]) events data from dxFeed API with period [UPDATE_DELAY] * * Loop will be stopped if [connected] flag will be set to false * * Method also contains a second loop that will repeat requests at [REQUEST_DELAY] intervals if an error occurs during the request */ private fun requester() { try { _errorFlow.tryEmit(null) loaded = false while (connected) { while (!loaded) { requestEvents() try { Thread.sleep(REQUEST_DELAY) } catch (e: InterruptedException) { _errorFlow.tryEmit(EventsProviderError.InterruptionError("Requester loop interrupted by thread interruption")) } } try { Thread.sleep(UPDATE_DELAY) } catch (e: InterruptedException) { _errorFlow.tryEmit(EventsProviderError.InterruptionError("Requester loop interrupted by thread interruption")) } } executorService?.shutdown() } catch (e: Exception) { _errorFlow.tryEmit(EventsProviderError.UnknownError("Requester loop interrupted: $e.message", e)) Log.e(TAG, "Requester loop interrupted", e) } } /** * Method for requesting and processing events data from dxFeed API * * Method sends a request with the current symbol of the instrument ([symbol]) ([symbol]) * with 3 paths - [CORPORATE_ACTIONS], [EARNING] using [requestCorporateActions] and [requestEarnings] methods. * * Query responses results are put into objects [EventsResponse] * * Then the data is parsed and divided into objects [DxFeedCorporateAction] and [DxFeedEarning] * * The next step is the conversion and normalization of data into data classes used by the library [Events.SplitsEvent], [Events.DividendsEvent], [Events.EarningsEvent], [Events.ConferenceCallsEvent] with extensions [convertAndNormalizeSplits], [convertAndNormalizeDividends], [convertAndNormalizeEarnings], [convertAndNormalizeCalls] with extensions [convertAndNormalizeSplits], [convertAndNormalizeDividends], [convertAndNormalizeEarnings], [convertAndNormalizeCalls] * * After this, the data is transferred to [Events] object and sent to [dataFlow] */ private fun requestEvents() { val corporateActionsResponse = requestCorporateActions() val earningsAndCallsResponse = requestEarnings() val earningsAndCalls = if (earningsAndCallsResponse.status == OK) { parseListData(earningsAndCallsResponse.data ?: "[]") } else emptyList() val dividendsAndSplits = if (corporateActionsResponse.status == OK) { parseListData(corporateActionsResponse.data ?: "[]") } else emptyList() val earnings = earningsAndCalls.filter { it.isEarning() } val conferenceCalls = earningsAndCalls.filter { it.isConferenceCall() } val dividends = dividendsAndSplits.filter { it.isDividend() } val splits = dividendsAndSplits.filter { it.isStockSplit() } _dataFlow.tryEmit( Events( dividends = dividends.convertAndNormalizeDividends(), splits = splits.convertAndNormalizeSplits(), conferenceCalls = conferenceCalls.convertAndNormalizeCalls(), earnings = earnings.convertAndNormalizeEarnings() ).apply { Log.d(TAG, "events loaded") } ) if (earningsAndCallsResponse.status == OK || corporateActionsResponse.status == OK) { loaded = true } } /** * Method for requesting corporate actions data from dxFeed API * * Sends a request for dividends and splits data with the current instrument symbol ([symbol]) to the path [URL] + [CORPORATE_ACTIONS] * * The request is made using [OkHttpClient] [client] and [Request] [request] * * @return [EventsResponse] with status [EventsResponse.Status.OK] and data if request was successful or with status [EventsResponse.Status.FAILED] and message if request was failed */ private fun requestCorporateActions(): EventsResponse { val corpActionsRequest = request.url(url + CORPORATE_ACTIONS + symbol).build() val corpActionsCall = client.newCall(corpActionsRequest) return try { corpActionsCall.execute().use { response -> if (response.isSuccessful) { val body = response.body?.string() ?: return EventsResponse(status = EventsResponse.Status.EMPTY) Log.d(TAG, "corporate actions loaded") EventsResponse(status = OK, data = body) } else { _errorFlow.tryEmit(EventsProviderError.NetworkError("Corporate actions request failed: $response.message $response.code")) EventsResponse( status = EventsResponse.Status.FAILED, message = "$response.message $response.code" ) } } } catch (e: Exception) { _errorFlow.tryEmit(EventsProviderError.NetworkError("Corporate actions request exception: $e.message")) EventsResponse( status = EventsResponse.Status.FAILED, message = e.message ) } } /** * Method for requesting earnings data from dxFeed API * * Sends a request for conference calls data with the current instrument symbol ([symbol]) to the path [url] + [EARNING] * * The request is made using [OkHttpClient] [client] and [Request] [request] * * @return [EventsResponse] with status [EventsResponse.Status.OK] and data if request was successful or with status [EventsResponse.Status.FAILED] and message if request was failed */ private fun requestEarnings(): EventsResponse { val earningRequest = request.url(url + EARNING + symbol).build() val earningCall = client.newCall(earningRequest) return try { earningCall.execute().use { response -> if (response.isSuccessful) { val body = response.body?.string() ?: return EventsResponse(status = EventsResponse.Status.EMPTY) Log.d(TAG, "earnings loaded") return EventsResponse(status = OK, data = body) } else { _errorFlow.tryEmit(EventsProviderError.NetworkError("Earning reports request failed: $response.message $response.code")) EventsResponse( status = EventsResponse.Status.FAILED, message = "$response.message $response.code" ) } } } catch (e: Exception) { _errorFlow.tryEmit(EventsProviderError.NetworkError("Earning reports request exception: $e.message")) EventsResponse( status = EventsResponse.Status.FAILED, message = e.message ) } } /** * Internal data class for storing response from dxFeed API * * @param status status of the response * @param data data of the response * @param message message of the response */ private data class EventsResponse( val status: Status, val data: T? = null, val message: String? = null ) { /** * Status of the response */ enum class Status { OK, FAILED, EMPTY } } /** * Extension function for parsing json string to list of objects of type [T] * * @param str json string * @return list of objects */ @Suppress("UNCHECKED_CAST") private inline fun parseListData(str: String): List { val type = TypeToken.getParameterized(List::class.java, T::class.java) return try { gson.fromJson(str, type) as List? ?: emptyList() } catch (e: Exception) { _errorFlow.tryEmit(EventsProviderError.ParsingError("Parsing error: $e.message", e)) emptyList() } } /** * Extension function for converting and normalizing list of [DxFeedEarning] to list of [Events.EarningsEvent] */ private fun List.convertAndNormalizeEarnings(): List { return filter { !it.eps.isNaN() && !it.estimatedEPS.isNaN() } .map { Events.EarningsEvent( basic = it.estimatedEPS, diluted = it.eps, periodEnding = it.ymd.parseYmd(), timestamp = it.ymd.parseYmd() ) } .distinctBy { it.timestamp } } /** * Extension function for converting and normalizing list of [DxFeedEarning] to list of [Events.ConferenceCallsEvent] * * @return list of [Events.ConferenceCallsEvent] */ private fun List.convertAndNormalizeCalls(): List { return map { Events.ConferenceCallsEvent( title = "$it.referencePeriod $it.announceTime.formatEventType()", timestamp = it.ymd.parseYmd(), date = it.ymd.parseYmd() ) }.distinctBy { it.timestamp } } /** * Extension function for converting and normalizing list of [DxFeedCorporateAction] to list of [Events.SplitsEvent] * * @return list of [Events.SplitsEvent] */ private fun List.convertAndNormalizeSplits(): List { return map { Events.SplitsEvent( splitFrom = it.ext?.splitFrom ?: 0f, splitTo = it.ext?.splitTo ?: 0f, timestamp = it.exYmd.parseYmd() ) }.distinctBy { it.timestamp } } /** * Extension function for converting and normalizing list of [DxFeedCorporateAction] to list of [Events.DividendsEvent] * * @return list of [Events.DividendsEvent] */ private fun List.convertAndNormalizeDividends(): List { return map { Events.DividendsEvent( gross = it.adjustmentValue ?: 0f, timestamp = it.exYmd.parseYmd() ) } } /** * Extension function for converting [Int] in format YYYYMMDD to [Long] timestamp in milliseconds * * @return timestamp in milliseconds */ private fun Int.parseYmd(): Long { val year = this / 10000 val month = (this - year * 10000) / 100 val day = this - year * 10000 - month * 100 return Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply { set(Calendar.YEAR, year) set(Calendar.MONTH, month - 1) set(Calendar.DAY_OF_MONTH, day) set(Calendar.HOUR_OF_DAY, 0) set(Calendar.MINUTE, 0) set(Calendar.SECOND, 0) set(Calendar.MILLISECOND, 0) }.timeInMillis } companion object { const val TAG = "DxFeedEventsProvider" const val REQUEST_DELAY = 2000L private const val UPDATE_DELAY = 10000L private const val CORPORATE_ACTIONS = "corporate-action/symbol/" private const val EARNING = "earning/symbol/" } } fun String.formatEventType(): String { return this.lowercase() .split("_") .joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } } .plus(" Webcast") } /** * Sealed class for representing different types of errors in the DxFeedEventsProvider. */ sealed class EventsProviderError(override val message: String, override val error: Throwable?) : ProviderError { data class NetworkError( override val message: String, override val error: Throwable? = null ) : EventsProviderError(message, error) data class InterruptionError( override val message: String, override val error: Throwable? = null ) : EventsProviderError(message, error) data class ParsingError( override val message: String, override val error: Throwable? = null ) : EventsProviderError(message, error) data class UnknownError( override val message: String, override val error: Throwable? = null ) : EventsProviderError(message, error) } `} --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/IPF Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # IPF Provider Provider supplying a list of data about trading instruments that can be set on the chart, along with their trading hours. {` /** * Interface for receiving instrument profile data - list of [InstrumentData] * * When loading dxCharts library, a list of instruments, their descriptions, etc. are taken from this interface * * When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.ipf.DxFeedHttpIpfProvider] and pass it to the library using [DxChartsDataProviders] data class * * Use [dataFlow] to get instrument profile data */ interface DxChartsIPFProvider { /** * Flow of receiving instrument profile data * * Instrument profile data is represented by list of [InstrumentData] */ val dataFlow: StateFlow> val isLoading: StateFlow } `} Data is sent by updating the state of the `dataFlow` variable. It is represented as a list of `com.devexperts.dxcharts.provider.domain.InstrumentData` with objects, which stores all data about the selected instrument: {` /** * Data class for storing data about instruments * * @param type Type of instrument (f.e. STOCK, CRYPTO) * @param symbol Symbol of instrument (f.e. GOOG, TSLA, AAPL) * @param description Description of instrument (f.e. Alphabet Inc. - Class C Capital Stock) * @param country Short name of a country of the instrument (f.e. US) * @param currency Short name of a currency of the instrument (f.e. USD) * @param priceIncrements Minimal price increments of the instrument (f.e. 0.01) * @param tradingHours Trading hours of the instrument. (e.g., "BIST(name=BIST;tz=Asia/Istanbul;hd=TR;sd=TR;td=12345;de=+0000;0=10001800)") */ data class InstrumentData( val type: String, val symbol: String, val description: String, val country: String, val currency: String, val priceIncrements: String, val tradingHours: String, ) `} Received data about instruments is displayed in the list of instruments: ![image-2024-1-29_0-40-22](../../../../images/instruments.png) Here is the default implementation of **`DxChartsIPFProvider`**: {` /** * Default implementation of [DxChartsIPFProvider] that uses HTTP requests to retrieve instrument data * and trading hours information from the DxFeed API. * * This class provides a data flow of [InstrumentData] through a [StateFlow] and periodically * updates the data by making HTTP requests to the DxFeed API. It also retrieves and merges * trading hours information for the instruments. * * @property gson Gson instance for JSON parsing. * @property _dataFlow Internal [MutableStateFlow] for emitting and collecting the instrument data. * @property dataFlow Public [StateFlow] that exposes the instrument data to external observers. * @property client [OkHttpClient] for making HTTP requests. * @property request HTTP request configuration for retrieving instrument data. * @property loaded Flag indicating whether data has been loaded successfully. * @property _errorFlow Internal [MutableStateFlow] for sending errors. * @property errorFlow [StateFlow] for sending errors. * @property job Job instance for managing the coroutine responsible for checking data. */ class DxFeedHttpIpfProvider( url: String = "", token: String = "", ) : DxChartsIPFProvider, DxChartsErrorProvider { private val gson = Gson() private val _dataFlow: MutableStateFlow> = MutableStateFlow(emptyList()) override val dataFlow: StateFlow> get() = _dataFlow private val client = OkHttpClient() private val request = Request.Builder() .url(url) .addHeader("Authorization", token) .build() @Volatile private var loaded = false private val _isLoading: MutableStateFlow = MutableStateFlow(false) override val isLoading: StateFlow get() = _isLoading private val _errorFlow: MutableStateFlow = MutableStateFlow(null) override val errorFlow: StateFlow get() = _errorFlow private var job: Job? = null /** * Initiates the process of requesting data from the DxFeed service. * * This method launches a coroutine in the background using [Dispatchers.IO] that periodically fetches data * until [loaded] is true or data is received in [dataFlow]. * * If a previous job is still active, it is cancelled before starting a new one. * The coroutine continues fetching data in a loop, emitting the loading state via [_isLoading], * calling [request] for data, and adding a delay of [FAIL_PAUSE_MS] between attempts. * * If an error occurs during the process, it is captured and emitted through [_errorFlow]. */ fun connect() { _errorFlow.tryEmit(null) if (job != null) { job?.cancel() job = null } job = CoroutineScope(Dispatchers.IO).launch { try { while (!loaded && dataFlow.value.isEmpty()) { _isLoading.emit(true) request() _isLoading.emit(false) delay(FAIL_PAUSE_MS) } } catch (e: Exception) { _errorFlow.tryEmit(IpfProviderError.UnknownError("Failed to fetch data: $e.message")) } } } /** * Stops the coroutine responsible for data retrieval. * * This method cancels the running [job] if it exists, terminating the ongoing data retrieval process. * * If an error occurs during the cancellation process, it is captured and emitted through [_errorFlow]. */ fun disconnect() { try { job?.cancel() job = null } catch (e: Exception) { _errorFlow.tryEmit(IpfProviderError.UnknownError("Failed to shutdown executor service: $e.message")) } } /** * Makes an HTTP request to the DXFeed API to fetch instrument data. * If the request is successful, it parses the data and updates the StateFlow. * If the request fails, it logs an error message. */ private fun request() { val call = client.newCall(request) try { call.execute().use { response -> if (response.isSuccessful) { val body = response.body?.string() ?: return val instruments = body.parseData() _dataFlow.tryEmit(instruments) loaded = true } else { _errorFlow.tryEmit(IpfProviderError.HttpError("IPF request failed with status code $response.code: $response.message")) } } } catch (e: IOException) { _errorFlow.tryEmit(IpfProviderError.NetworkError("IPF request failed - Network Error: $e.message")) } } /** * Parses the raw data string and converts it into a list of [InstrumentData] objects. * * @return List of [InstrumentData] parsed from the raw data string. */ private fun String.parseData(): List { val result = mutableListOf() val typeMappings = mutableMapOf>() try { this.lines().forEach { line -> if (line.startsWith("#")) { val parts = line.removePrefix("#").split("::=") if (parts.size == 2) { val type = parts[0].trim() val headers = parts[1].split(",") typeMappings[type] = headers.withIndex().associate { it.value.trim() to it.index } } } else if (line.isNotBlank()) { val parts = splitWithQuotes(line) val type = parts[0] val mappings = typeMappings[type] if (mappings != null) { result.add( InstrumentData( type = parts[mappings["TYPE"] ?: 0], symbol = parts[mappings["SYMBOL"] ?: 1], description = parts.getOrElse(mappings["DESCRIPTION"] ?: -1) { "" }, country = parts.getOrElse(mappings["COUNTRY"] ?: -1) { "" }, currency = parts.getOrElse(mappings["CURRENCY"] ?: -1) { "" }, priceIncrements = parts.getOrElse( mappings["PRICE_INCREMENTS"] ?: -1 ) { "" }, tradingHours = parts.getOrElse( mappings["TRADING_HOURS"] ?: -1 ) { "" }, ) ) } } } } catch (e: Exception) { _errorFlow.tryEmit( IpfProviderError.ParsingError( "Parsing data failed: $e.message", e ) ) } return result } /** * Splits a string by commas, ignoring commas inside quotes. If there are empty elements between commas, * an empty string is added to the result. * * This function is used to process CSV-like strings where some elements may be enclosed in quotes * (so that commas inside the values are not treated as separators). It also correctly handles empty fields * between commas by inserting empty strings where necessary. * * @param line The input string to be split. The string may contain quoted elements, and empty fields between commas. * @return A list of strings split by commas, taking into account quoted sections and empty fields between commas. * * Example: * For the input string: * "STOCK,BASFY,BASF SE S/ADR by BASF SE,US,OOTC,,USD,," * The function will return the list: * ["STOCK", "BASFY", "BASF SE S/ADR by BASF SE", "US", "OOTC", "", "USD", ""] */ private fun splitWithQuotes(line: String): List { val result = mutableListOf() val regex = Pattern.compile("""("([^"]*)"|([^,]*))(,|$)""") val matcher = regex.matcher(line) while (matcher.find()) { val value = matcher.group(2) ?: matcher.group(3) ?: "" result.add(value) } val finalResult = mutableListOf() var currentIndex = 0 for (part in result) { while (line.indexOf(",", currentIndex) > line.indexOf( part, currentIndex ) + part.length ) { finalResult.add("") } finalResult.add(part) currentIndex = line.indexOf(part, currentIndex) + part.length } return finalResult } /** * Companion object containing constants and configuration for the DxFeedHttpIpfProvider class. * * @property TAG Logging tag for the class. * @property FAIL_PAUSE_MS Time to pause in case of a failed IPF request. */ companion object { private const val TAG = "DxFeedHttpIpfProvider" private const val FAIL_PAUSE_MS = 3000L } } /** * Sealed class representing different types of errors that can occur in the DxFeedHttpIpfProvider. */ sealed class IpfProviderError(override val message: String, override val error: Throwable?) : ProviderError { data class NetworkError( override val message: String, override val error: Throwable? = null ) : IpfProviderError(message, error) data class ParsingError( override val message: String, override val error: Throwable? = null ) : IpfProviderError(message, error) data class HttpError( override val message: String, override val error: Throwable? = null ) : IpfProviderError(message, error) data class UnknownError( override val message: String, override val error: Throwable? = null ) : IpfProviderError(message, error) } `} --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/News Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # News Provider Provider supplies data about news related to a specific trading instrument. {` /** * Interface for receiving news data * * When loading dxCharts library chart, news events are taken from this interface * * When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.news.DxFeedNewsProvider] and pass it to the library using [DxChartsDataProviders] data class * * Use [dataFlow] to get news data as list of [NewsData.NewsItem] * * Use [changeSymbol] to change news instrument symbol */ interface DxChartsNewsProvider { /** * Flow of receiving news data * * News data is represented by list of [NewsData.NewsItem] */ val dataFlow: StateFlow> /** * Changes news instrument symbol * * @param symbol instrument symbol */ fun changeSymbol(symbol: String) } `} ### Method: changeSymbol This method is used to change the symbol of the instrument for which the news data is provided. It takes the following parameter: - `symbol`: The symbol of the selected instrument. A symbol is a unique identifier used for trading on the exchange. For example, the symbol for Apple stocks on NASDAQ is "AAPL". #### Example Here is an example of how to use the `changeSymbol` method: {` newsProvider.changeSymbol("AAPL") `} Data is sent by updating the state of the `dataFlow` variable. It is represented as a list of `com.devexperts.dxcharts.provider.domain.NewsData.NewsItem` objects, which stores data about the news of the selected instrument: {` /** * Data class for storing data about a single news item. * * @param title Title of news item that will be shown in the chart (f.e. Apple Inc. (AAPL) Q4 2021 Earnings Call Transcript) * @param timestamp Timestamp in milliseconds of news item * @param source String with url of the news. If null, the news item will not be clickable. If not null, the news item will be clickable and will open the url in the browser */ data class NewsItem( val title: String, val timestamp: Long, val source: String?, ) `} News items are displayed on the chart: ![news_dot](../../../../images/news_dot.png) ![news_card](../../../../images/news_card.png) Upon clicking the news card, there is a redirection to an external URL with the parameter `NewsItem.source`. Here is the default implementation of **`DxChartsNewsProvider`**: {` /** * Default implementation of [DxChartsNewsProvider]. * * Uses dxFeed API to get demo news data. * * Limit for list news - 20 items [NEWS_LIMIT] * * Uses the DxFeed api via Http requests using [OkHttpClient]. * * A token [token] is used for authorization with Base64 encoding. * * When loading dxCharts library and starting the provider ([changeSymbol]), a requests are sent with the current symbol of the instrument ([symbol]). * * Query responses results are parsed with [parseData] method and put into [NewsData] objects. * * Requests occurs once every 10 seconds ([UPDATE_DELAY]). * If an error occurs during the request, it is repeated every 500 milliseconds ([REQUEST_DELAY]). * * The data retrieval logic is executed in a loop in a separate thread using [ExecutorService] [executorService]. Loop is working until [connected] flag is true. * * Changing the symbol of the instrument and starting the work of the provider occurs using [changeSymbol] method - if the provider has already been started, then the thread with it stops, a new thread with a new symbol is started. * * Inside a separate thread [requester] method id executed - method that repeatedly executes queries and processes responses every [UPDATE_DELAY] ms. * * Inside [request] method there is a looped [requestNews] method that sends requests and parse responses. * * To manually stop the provider, you can use [disconnect] method. It stops the provider, cleans [executorService] and disconnects from dxFeed API. * * @property gson [Gson] object for parsing responses. * @property executorService [ExecutorService] for executing requests in a separate thread. * @property dataFlow public [StateFlow] with [List] of [NewsData.NewsItem] objects. * @property _dataFlow internal [MutableStateFlow] with [List] of [NewsData.NewsItem] objects. * @property client [OkHttpClient] for sending requests. * @property request [Request] for sending requests with "Authorization" header with [token]. * @property loaded loaded flag that shows if data was successfully loaded in [requester] method. * @property connected connected flag that shows if provider is connected to dxFeed API. * @property symbol current symbol of selected instrument. * @property _errorFlow Internal [MutableStateFlow] for sending errors. * @property errorFlow [StateFlow] for sending errors. */ class DxFeedNewsProvider( private val url: String = "", private val token: String = "", ) : DxChartsNewsProvider, DxChartsErrorProvider { override val dataFlow: StateFlow> get() = _dataFlow private val _dataFlow = MutableStateFlow>(emptyList()) private val gson = Gson() private var executorService: ExecutorService? = null private val client = OkHttpClient() .newBuilder() .build() private val request = token.let { Request.Builder() .addHeader("Authorization", it) } private val _errorFlow = MutableStateFlow(null) override val errorFlow: StateFlow get() = _errorFlow @Volatile private var loaded = false @Volatile private var connected = false @Volatile private var symbol: String? = null /** * Changes the symbol of the instrument and starts / restarts the work of the provider. * * If the provider and [executorService] are running, the method stops it (shutdownNow()). * * The passed parameter [symbol] is set to the value [symbol]. * * [connected] flag sets to true * * The [requester] method is passed to [executorService] to run in a new thread. * * @value value New instrument symbol for requesting news */ override fun changeSymbol(symbol: String) { executorService?.let { if (!it.isShutdown) { try { it.shutdownNow() } catch (_: Exception) { } } } this.symbol = symbol _dataFlow.tryEmit(emptyList()) connected = true executorService = Executors.newFixedThreadPool(1) executorService?.execute { requester(symbol) } } /** * Method in a loop executes the [requestNews] method every 10 seconds [UPDATE_DELAY] until [connected] == true * * At the beginning of the method, the [loaded] value is set to true * * If an error occurs when executing the [requestNews] method and [loaded] == false, then retries will be performed every 500 ms. [REQUEST_DELAY] */ private fun requester(symbol: String) { try { loaded = false while (connected) { while (!loaded) { requestNews() try { Thread.sleep(REQUEST_DELAY) } catch (e: InterruptedException) { _errorFlow.tryEmit(NewsProviderError.InterruptionError("Requester loop interrupted by thread interruption")) } } try { Thread.sleep(UPDATE_DELAY) } catch (e: InterruptedException) { _errorFlow.tryEmit(NewsProviderError.InterruptionError("Requester loop interrupted by thread interruption")) } } executorService?.shutdown() } catch (e: Exception) { Log.i(TAG, "Request for getting news with symbol $symbol has interrupted") } } /** * Method for manually stopping the provider, turning off [executorService]. * * [connected] sets to false * * [loaded] sets to true */ fun disconnect() { connected = false loaded = true try { executorService?.shutdownNow() } catch (_: Exception) { } } /** * Method for executing a request in dxFeed API to obtain a list of news based on the passed instrument symbol [symbol] * * Path for the request is calculating in the [urlWithSymbol] method where [symbol] is passed as an argument * * The request is made using [OkHttpClient] [client] via [request] with authorization token [token] in Base64 encoding * * If the request is successful, [loaded] flag is set to true, the data is parsed using [parseData] method and emitted to [dataFlow] */ private fun requestNews() { val req = request.url(urlWithSymbol(symbol.orEmpty())).build() val call = client.newCall(req) try { call.execute().use { response -> when { response.isSuccessful -> { val body = response.body?.string() ?: return val parsedData = body.parseData() if (parsedData.isNotEmpty()) { _dataFlow.tryEmit(parsedData) loaded = true } else { _errorFlow.tryEmit(NewsProviderError.ParseError("No news items found")) } } response.code == 401 -> { _errorFlow.tryEmit(NewsProviderError.UnauthorizedError("Unauthorized: response.message")) } response.code in 500..599 -> { _errorFlow.tryEmit(NewsProviderError.NetworkError("Server error: $response.message")) } else -> { _errorFlow.tryEmit(NewsProviderError.UnknownError("Error: $response.message")) } } } } catch (e: Exception) { _errorFlow.tryEmit(NewsProviderError.NetworkError("Failed to execute request: $e.message")) } } /** * Extension parses [String] data using [gson] into the list of [NewsData.NewsItem] objects * @return parsed news data */ private fun String.parseData(): List { val linkRegex = """""".toRegex() return try { val dxFeedNewsData = gson.fromJson(this, DxFeedNewsData::class.java) dxFeedNewsData.news.map { newsItem -> val url = linkRegex.find(newsItem.body)?.groupValues?.getOrNull(1) NewsData.NewsItem( title = newsItem.title, timestamp = newsItem.time.time, source = url ) } } catch (e: Exception) { Log.e(TAG, "Failed to parse news data", e) _errorFlow.tryEmit(NewsProviderError.ParseError("Failed to parse news data: $e.message")) emptyList() } } /** * @return Full URL Path to get news from dxFeed API with [url], [symbol] and [NEWS_LIMIT] params */ private fun urlWithSymbol(symbol: String) = "$url?symbol=$symbol&limit=$NEWS_LIMIT" companion object { const val TAG = "DxFeedNewsProvider" const val NEWS_LIMIT = 20 const val REQUEST_DELAY = 500L private const val UPDATE_DELAY = 10000L } } /** * Sealed class for representing different types of errors in DxFeedNewsProvider. */ sealed class NewsProviderError(override val message: String, override val error: Throwable?) : ProviderError { data class NetworkError( override val message: String, override val error: Throwable? = null ) : NewsProviderError(message, error) data class ParseError( override val message: String, override val error: Throwable? = null ) : NewsProviderError(message, error) data class InterruptionError( override val message: String, override val error: Throwable? = null ) : NewsProviderError(message, error) data class UnauthorizedError( override val message: String, override val error: Throwable? = null ) : NewsProviderError(message, error) data class UnknownError( override val message: String, override val error: Throwable? = null ) : NewsProviderError(message, error) } `} --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/Quotes Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Quotes Provider Provider supplies quotes of the selected trading instrument, to display this information in the trading widget. {` /** * Interface for receiving quotes data * * When loading dxCharts library chart, quotes data is taken from this interface * * When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.quotes.DxFeedQuotesProvider] and pass it to the library using [DxChartsDataProviders] data class * * Use [dataFlow] to get quotes data * * Use [changeSymbol] to change quotes' instrument symbol */ interface DxChartsQuotesProvider { /** * Flow of receiving quotes data * * Quotes data is represented by [Quote] */ val dataFlow: StateFlow /** * Changes quotes' instrument symbol * * @param symbol instrument symbol */ fun changeSymbol(symbol: String) } `} With `changeSymbol` method, the symbol of the instrument can be changed. Data is sent by updating the state of the `dataFlow` variable. It is represented as `com.devexperts.dxcharts.provider.domain.Quote` object. {` /** * Quote - data with instruments' statistics from exchange * * Stores ask, bid prices and sizes */ data class Quote( val bidPrice: Double, val askPrice: Double, ) `} Trading Widget with the quotes passed into it: ![trading](../../../../images/trading.png) Here is the default implementation of **`DxChartsQuotesProvider`**: {` /** * Implementation of [DxChartsQuotesProvider] that uses the dxFeed API. * * The library "com.devexperts.qd:qds" is used to connect to the dxFeed API. * * The process of obtaining quotes is performed in a separate thread [ExecutorService] using [endpoint] from method [connect]: * - [endpoint] connects to the dxFeed API at the [endpointAddress] in the [connect] method. * - A subscription [sub] is created to receive quotes. * - An [eventListener] is added to the subscription [sub] to handle the received quotes. * - When a quote is received in [eventListener], it is transformed into [com.devexperts.dxcharts.provider.domain.Quote] * using the extension function [toDataObject] and sent to [dataFlow]. * - Errors encountered during operations are reported via [errorFlow], using instances of [QuoteProviderError]. * * When the symbol changes [changeSymbol], the subscription [sub] is disconnected from the old symbol and connected to the new one. * * When disconnecting [disconnect] from the dxFeed API, the subscription [sub] is disconnected from the symbol and cleared. * * @property [endpoint] Object for working with the dxFeed API. * @property [executorService] Object for starting a thread to retrieve quotes. * @property [sub] Subscription object of [endpoint] with the instrument symbol [currentSymbol]. * @property [currentSymbol] Current symbol of the instrument. * @property [_dataFlow] Internal [MutableStateFlow] for sending quotes. * @property [dataFlow] [StateFlow] for sending quotes. * @property [_errorFlow] Internal [MutableStateFlow] for sending errors. * @property [errorFlow] [StateFlow] for sending errors. * @property [eventListener] Handler for quotes received from [sub]. */ class DxFeedQuotesProvider(private val endpointAddress: String = "") : DxChartsQuotesProvider, DxChartsErrorProvider { private val endpoint = DXEndpoint.getInstance() private val sub: DXFeedSubscription = endpoint.feed.createSubscription(Quote::class.java) private val _dataFlow = MutableStateFlow(null) override val dataFlow: StateFlow get() = _dataFlow private val _errorFlow = MutableStateFlow(null) override val errorFlow: StateFlow get() = _errorFlow private val eventListener = DXFeedEventListener { try { val quote = it.last() _dataFlow.tryEmit(quote?.toDataObject()) } catch (e: Exception) { _errorFlow.tryEmit(QuoteProviderError.DataProcessingError("Error during processing quotes: $e.message", e)) } } private var currentSymbol: String? = null private var executorService: ExecutorService? = null /** * Connects to the dxFeed API [endpoint] at the [endpointAddress], * subscribes [sub] to receive quotes, connects [eventListener], * and starts the process of obtaining quotes in a separate thread [executorService]. * Handles connection errors and updates [_errorFlow]. */ fun connect() { try { _errorFlow.tryEmit(null) endpoint.connect(endpointAddress) sub.addEventListener(eventListener) if (executorService == null || executorService?.isShutdown == true) { executorService = Executors.newFixedThreadPool(1) executorService?.execute { try { endpoint.awaitNotConnected() } catch (e: Exception) { _errorFlow.tryEmit(QuoteProviderError.NetworkError("Connection to quote provider was interrupted: $e.message}", e)) } } } else { Log.w(TAG, "ExecutorService is already running.") } } catch (e: Exception) { _errorFlow.tryEmit( QuoteProviderError.ConnectionError("Unknown error during connection to quote provider: $e.message", e) ) } } /** * Removes [eventListener] from [sub], clears [sub] from [endpoint], * disconnects [endpoint] from the dxFeed API, and shuts down [executorService]. * Handles disconnection errors and updates [_errorFlow]. */ fun disconnect() { try { sub.removeEventListener(eventListener) endpoint.feed.detachSubscriptionAndClear(sub) endpoint.disconnectAndClear() executorService?.shutdown() executorService = null } catch (e: Exception) { _errorFlow.tryEmit( QuoteProviderError.ConnectionError("Unknown error during disconnection of quote provider $e.message", e) ) } } /** * Extension function for transforming [Quote] into [com.devexperts.dxcharts.provider.domain.Quote]. */ private fun Quote.toDataObject() = com.devexperts.dxcharts.provider.domain.Quote( bidPrice = bidPrice, askPrice = askPrice, ) /** * Method to change the instrument symbol [currentSymbol] in the subscription [sub]. * * When the symbol changes [currentSymbol], the subscription [sub] is disconnected from the old symbol, * connected to the new one, and [dataFlow] is cleared. */ override fun changeSymbol(symbol: String) { if (currentSymbol == symbol) { return } currentSymbol?.let { try { sub.removeSymbols(currentSymbol) _dataFlow.tryEmit(null) } catch (e: Exception) { _errorFlow.tryEmit( QuoteProviderError.DataProcessingError( "Error during processing quotes: $e.message", e ) ) } } currentSymbol = symbol try { sub.addSymbols(symbol) } catch (e: Exception) { _errorFlow.tryEmit( QuoteProviderError.DataProcessingError("Error adding symbol: $e.message", e) ) } } companion object { private const val TAG = "DxFeedQuoteProvider" } } /** * Sealed class representing different types of errors that can occur in the provider. */ sealed class QuoteProviderError(override val message: String, override val error: Throwable?) : ProviderError { data class NetworkError( override val message: String, override val error: Throwable? = null ) : QuoteProviderError(message, error) data class ConnectionError( override val message: String, override val error: Throwable? = null ) : QuoteProviderError(message, error) data class DataProcessingError( override val message: String, override val error: Throwable? = null ) : QuoteProviderError(message, error) } `} --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/Schedule Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Schedule Provider The **Schedule Provider** is an interface that defines the contract for fetching trading schedule information for use in DXCharts. It provides the ability to retrieve details about trading sessions, their types, and the trading days they belong to. The implementation of this interface can retrieve data from remote services, databases, or other sources. ## Interface Definition {` /** * Interface defining the contract for providing trading schedules for use in DXCharts. * * This interface allows implementing classes to fetch and provide detailed information about trading schedules * based on a specified trading hours identifier. Implementations of this interface may retrieve data from various * sources such as local databases, remote services, or in-memory caches. */ interface DxChartsScheduleProvider { /** * Retrieves the trading schedule associated with the specified trading hours identifier. * * This method is designed to be used asynchronously, allowing the calling code to wait for the result * without blocking the main thread. The implementation should handle any necessary data fetching, parsing, * and error handling. * * @param tradingHours A string identifier representing the specific trading hours for which to fetch the schedule. * This value should correspond to a valid trading hours identifier understood by the provider. * @return A [Schedule] object containing detailed trading schedule information, or null if no data is available * or an error occurs during the operation. */ suspend fun getSchedule(tradingHours: String): Schedule? } `} When an instrument is changed on the chart, data is fetched by calling the `getSchedule` function, which returns the corresponding schedule. ## Schedule Data Structure The `Schedule` class represents the processed information about an instrument's trading hours. Each trading day consists of multiple sessions, and these sessions are categorized by type. {` /** * Data class for storing processed information about instrument's trading hours * * Trading hours are split by sessions [Schedule.Day.Session] * * Every session belongs to a day [Schedule.Day] * * Sessions can be of different types [Schedule.Day.Session.Type] (PRE_MARKET, REGULAR, AFTER_MARKET, NO_TRADING) * * @property name Name of the instrument. * @property timeZone Time zone name of the instrument. * @property days List of trading days. */ data class Schedule( val name: String, val timeZone: String, val days: List ) { /** * Data class for storing processed information about instrument's trading hours for a single day * * Trading hours are split by sessions [Schedule.Day.Session] * * Day is split by sessions * * @property id ID of the day (e.g., 2021-11-08). * @property isHoliday Indicates whether the day is a holiday with no trading. * @property isShort Indicates whether the day is a short day with shorter trading hours. * @property sessions List of trading sessions. */ data class Day( val id: String, val isHoliday: Boolean, val isShort: Boolean, val sessions: List ) { /** * Data class for storing processed information about instrument's trading hours for a single session * * Trading hours are split by sessions * * Session belongs to a day [Schedule.Day] * * Sessions can be of different types [Schedule.Day.Session.Type] (PRE_MARKET, REGULAR, AFTER_MARKET, NO_TRADING) * * @property startTime Start time of the session in the format "yyyy-MM-dd HH:mm:ss'+0000'". * @property endTime End time of the session in the format "yyyy-MM-dd HH:mm:ss'+0000'". * @property type Type of the session (NO_TRADING, PRE_MARKET, REGULAR, AFTER_MARKET). */ data class Session( val startTime: String, val endTime: String, val type: Type, ) { enum class Type(val value: String) { NO_TRADING("NO_TRADING"), PRE_MARKET("PRE_MARKET"), REGULAR("REGULAR"), AFTER_MARKET("AFTER_MARKET") } } } } `} ## Extended Hours Visualization Extended hours are displayed on the chart as follows: ![image-2024-1-29_0-41-13](../../../../images/extended_hours.png) ## Implementation Example Below is a default implementation of the `DxChartsScheduleProvider` interface, which fetches schedule data using HTTP requests. {` /** * Implementation of [DxChartsScheduleProvider] that fetches schedule information using HTTP requests. * * This class provides a way to fetch trading schedule information from a remote service using HTTP POST requests. * The retrieved data is parsed into domain-specific objects for further use in the application. * * @property token Authorization token for accessing the schedule service. This token is included as a header in the requests. */ class DxFeedHttpScheduleProvider( private val token: String = "" ) : DxChartsScheduleProvider { private val client = OkHttpClient() private val gson = Gson() /** * Fetches a trading schedule based on the provided trading hours identifier. * * This method sends an HTTP POST request to the schedule service with the specified trading hours identifier. * If the response is successful, it parses the JSON response into a domain-specific [Schedule] object. * * @param tradingHours Identifier of the trading hours to fetch the schedule for. * This value is included in the request body as part of the "schedules" field. * @return A [Schedule] object containing trading schedule details or null if an error occurs or no data is returned. */ override suspend fun getSchedule(tradingHours: String): Schedule? { val requestBody = gson.toJson( SchedulesRequestBody( schedules = listOf(tradingHours) ) ).toRequestBody(JSON) val request = Request.Builder() .url(TRADING_HOURS_PARSER_URL) .addHeader("Authorization", token) .method("POST", requestBody) .build() val call = client.newCall(request) try { call.execute().use { response -> if (response.isSuccessful) { val body = response.body?.string() ?: return null val itemsMapType = object : TypeToken>>() {}.type val parsedData = gson.fromJson>>( body, itemsMapType ) return parsedData.firstOrNull()?.values?.firstOrNull()?.schedule?.toDomainSchedule() } else { return null } } } catch (e: IOException) { return null } } /** * Converts [SchedulesResponseItem.Schedule] to the domain-specific [Schedule] object. * * This method maps the internal representation of a schedule from the service response to the domain model used * in the application. It includes mapping for days and sessions within the schedule. * * @return The converted [Schedule] object. */ private fun SchedulesResponseItem.Schedule.toDomainSchedule(): Schedule { return Schedule( name = this.name, timeZone = this.timeZone, days = this.days.map { day -> Schedule.Day( id = day.id, isHoliday = day.isHoliday, isShort = day.isShort, sessions = day.sessions.map { session -> Schedule.Day.Session( startTime = session.startTime, endTime = session.endTime, type = when (session.type) { SchedulesResponseItem.Schedule.Day.Session.Type.NO_TRADING -> Schedule.Day.Session.Type.NO_TRADING SchedulesResponseItem.Schedule.Day.Session.Type.PRE_MARKET -> Schedule.Day.Session.Type.PRE_MARKET SchedulesResponseItem.Schedule.Day.Session.Type.REGULAR -> Schedule.Day.Session.Type.REGULAR SchedulesResponseItem.Schedule.Day.Session.Type.AFTER_MARKET -> Schedule.Day.Session.Type.AFTER_MARKET } ) } ) } ) } /** * Data class representing the request body for fetching trading schedules. * * @property schedules List of trading schedules to fetch. * @property start Start timestamp for the request. * @property timeZone Timezone for the request. */ private data class SchedulesRequestBody( @SerializedName("schedules") val schedules: List, @SerializedName("start") val start: Long = System.currentTimeMillis() - SCHEDULE_PERIOD_MILLS, @SerializedName("tz") val timezone: String = "UTC" ) /** * Data class representing the response item structure from the trading hours parser service. * * @property schedule schedule information containing trading days and sessions. */ private data class SchedulesResponseItem( val schedule: Schedule ) { /** * Data class representing the trading schedule structure. * * @property name Name of the schedule. * @property timeZone Timezone associated with the schedule. * @property days List of trading [Day]s. */ data class Schedule( val name: String, val timeZone: String, val days: List ) { /** * Data class representing a trading day within the schedule. * * @property id Identifier for the trading day. * @property isHoliday Indicates whether the day is a holiday. * @property isShort Indicates whether the trading day is shorter than usual. * @property sessions List of trading [Session]s within the day. */ data class Day( val id: String, val isHoliday: Boolean, val isShort: Boolean, val sessions: List ) { /** * Data class representing a trading session within a trading day. * * @property startTime Start time of the trading session. * @property endTime End time of the trading session. * @property type Type of the trading session (NO_TRADING, PRE_MARKET, REGULAR, AFTER_MARKET). */ data class Session( val startTime: String, val endTime: String, val type: Type, ) { /** * Enum class defining possible types of trading sessions. * * Values: [NO_TRADING], [PRE_MARKET], [REGULAR], [AFTER_MARKET] */ enum class Type { NO_TRADING, PRE_MARKET, REGULAR, AFTER_MARKET } } } } } /** * Companion object containing constants and configuration for the DxFeedHttpScheduleProvider class. * * @property JSON Media type for JSON requests. * @property SCHEDULE_PERIOD_MILLS Milliseconds in a year for scheduling purposes. * @property TRADING_HOURS_PARSER_URL URL for the trading hours parser service. */ companion object { private val JSON: MediaType = "application/json; charset=utf-8".toMediaTypeOrNull()!! private const val TRADING_HOURS_PARSER_URL = "https://tools.dxfeed.com/schedule-view/per-calendar-day" private const val SCHEDULE_PERIOD_MILLS = 1000L * 60L * 60L * 24L * 365L } } `} --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/Studies data Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Studies data Provider Provider that calculates indicators data and pass the calculated data to the library. {` /** * Interface for receiving calculated studies data * * When calculating studies, parameters selected by user are transferred to this interface, then calculated studies are received from here * * When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.studies.DxStudiesDataProviderImplementation] and pass it to the library using [DxChartsDataProviders] data class * * The [dataFlow] property is used to get calculated studies data. It is a StateFlow that emits a list of [StudiesData] objects representing the studies data. * * The [setCandles] method is used to set candles data for studies calculation. * * The [setStudiesList] method is used to set a list of studies configurations that has to be calculated. * * The [getEnabledStudies] method is used to get a list of studies configurations that has to be calculated. * * The [updateTradingSessions] method is used to update trading sessions for studies calculators. */ interface DxChartsStudiesDataProvider { /** * Flow of receiving calculated studies data * * This property is a StateFlow that emits a list of [StudiesData] objects. Each [StudiesData] object represents the calculated studies data for a particular study. */ val dataFlow: StateFlow> /** * Sets candles data for studies calculation * * This method is used to set the candles data that will be used for calculating the studies. The candles data is represented by a [DxChartsCandles] object. * * @param candles The candles data. */ fun setCandles(candles: DxChartsCandles) /** * Sets list of studies configurations that has to be calculated * * This method is used to set the list of studies configurations that will be calculated. Each configuration is represented by a [StudiesSetting] object. * * @param studies The list of studies configurations. */ fun setStudiesList(studies: List) /** * Gets list of studies configurations that has to be calculated * * This method is used to get the list of studies configurations that are currently being calculated. Each configuration is represented by a [StudiesSetting] object. * * @return The list of studies configurations. */ fun getEnabledStudies(): List /** * Updates trading sessions for studies calculators * * This method is used to update the trading sessions that will be used for calculating the studies. Each trading session is represented by a [TradingSession] object. * * @param sessions The list of trading sessions. */ fun updateTradingSessions(sessions: List) } `} Interface methods: - `setCandles`: Sets candles data for calculations. - `setStudiesList`: Sets a list of indicators and their settings. - `getEnabledStudies`: Returns a list of indicators that are currently being calculated in the provider. - `updateTradingSessions`: Sets a trading sessions data for calculating indicators. Data is sent by updating the state variable `dataFlow`. It is represented as a list of `com.devexperts.dxcharts.provider.StudiesData` objects. Incoming data is represented as `com.devexperts.dxcharts.provider.StudiesSetting` objects. {` /** * Data class for storing calculated studies data * * @param uuid uuid of the data * @param data DoubleArray with calculated data */ data class StudiesData( val uuid: String = "", val data: Array = emptyArray() ) /** * Data class representing the settings for a study in a financial indicator. * * @property id The unique identifier for the study setting. * @property title The title of the study. * @property uuid The universally unique identifier (UUID) for the study. * @property type The type of the indicator associated with the study. * @property parameters The list of parameters for the study. * @property lines The list of lines for the study. * @property overlaying A boolean indicating whether the study is overlaying on the chart. * @property calculateFutureData A boolean indicating whether to calculate future data for the study. * @property categories A string representing the categories associated with the study. * @property locked A nullable boolean indicating whether the study is locked. */ data class StudiesSetting( val id: String, val title: String, val uuid: String, val type: IndicatorType, val parameters: List, val lines: List, val overlaying: Boolean, val calculateFutureData: Boolean, val categories: String, val locked: Boolean? ) { /** * Data class representing a line associated with a study. * * @property title The title of the line. * @property type The type of the study line. * @property thickness The thickness of the line. * @property colors The list of colors associated with the line. * @property visible A boolean indicating whether the line is visible. */ data class Line( val title: String?, val type: Type?, val thickness: Int?, val colors: List?, val visible: Boolean? ) { /** * Enum class representing the types of study lines. */ enum class Type { POINTS, LINEAR, HISTOGRAM, DIFFERENCE, ABOVE_CANDLE_TEXT, TEXT, BELOW_CANDLE_TEXT, ABOVE_CANDLE_TRIANGLE, TRIANGLE, COLOR_CANDLE, RECTANGULAR; } } /** * Data class representing a parameter associated with a study. * * @property id The unique identifier for the parameter. * @property title The title of the parameter. * @property type The type of the study parameter. * @property value The value of the parameter. * @property validation The [Validation] rules for the parameter. * @property visible A boolean indicating whether the parameter is visible. */ data class Parameter( val id: String, val type: Type, val value: Any?, val validation: Validation?, val visible: Boolean? ) { /** * Data class representing validation rules for a study parameter. * * @property min The minimum value allowed for the parameter. * @property max The maximum value allowed for the parameter. * @property precision The precision of the parameter value. */ data class Validation( val min: Double?, val max: Double?, val precision: Int?, ) /** * Enum class representing the types of study parameters. */ enum class Type { INTEGER_RANGE, DOUBLE_RANGE, PRICE_FIELD, STRING, AGGREGATION, BOOLEAN, AVERAGE, UNDEFINED, } } } /** * Data class representing a trading session with time range and optional high and low values. * * [TradingSession] needed for some indicators (e.g. Volume Weighted Average Price) * * @property from The starting time of the trading session. * @property to The ending time of the trading session. * @property high The highest value during the trading session (nullable). * @property low The lowest value during the trading session (nullable). */ data class TradingSession( val from: Double, val to: Double, val high: Double? = null, val low: Double? = null ) `} Calculated indicators looks as follows: indicators Here is the default implementation of **`DxChartsStudiesDataProvider`**: {` /** * Implementation of the [DxChartsStudiesDataProvider] interface, providing functionality * to manage studies data and candles for DX charts. * * @property _dataFlow Internal mutable state flow to emit [StudiesData] to the UI. * @property dataFlow Exposed immutable state flow to observe [StudiesData] from the UI. * @property candlesState Internal mutable state flow to manage [DxChartsCandles]. * @property studiesListState Internal mutable state flow to manage the list of [StudiesSetting]. * @property sessionsFlow Internal mutable state flow to manage [TradingSession]s. * @property executorService Executor service for managing background tasks. * @property job Background job for executing data processing. * @property dxStudies Volatile variable to store the [DxStudies] data. * @property _errorFlow Internal [MutableStateFlow] for sending errors. * @property errorFlow [StateFlow] for sending errors. */ class DxStudiesDataProviderImplementation : DxChartsStudiesDataProvider, DxChartsErrorProvider { private val _dataFlow: MutableStateFlow> = MutableStateFlow(emptyList()) override val dataFlow: StateFlow> get() = _dataFlow private val candlesState: MutableStateFlow = MutableStateFlow(DxChartsCandles()) private val studiesListState: MutableStateFlow> = MutableStateFlow(emptyList()) private val sessionsFlow: MutableStateFlow> = MutableStateFlow(emptyList()) private val _errorFlow = MutableStateFlow(null) override val errorFlow: StateFlow get() = _errorFlow private var executorService: ExecutorService? = null private var job: Job? = null @Volatile private var dxStudies: DxStudies = DxStudies(0, emptyArray()) /** * Starts the background data processing job. * If a previous job exists, it is canceled before starting a new one. * Inside this method, a background job is executed using the [executorService]. * * When we get new candles, we update [dxStudies] using [updateDxStudies] function. * * When we get instrument we get trading sessions for it, that are necessary for some indicators. We need to provide them to [dxStudies] with [DxStudies.setTradingSessions] * * After that we take list of parameters of enabled studies and use [DxStudies.createStudy] with this parameters to create [Study]. Then we use [Study.calculateAll] to calculate studies data and create [StudiesData] with it. * * In the end we pass this calculated studies data to [_dataFlow] * * The data processing includes combining [DxChartsCandles], [StudiesSetting]s, and [TradingSession]s. * Delta candles are calculated based on the difference between new and old candle data. * Studies and trading session data are updated accordingly, and the result is emitted to the UI * through the [_dataFlow] state flow. */ fun connect() { if (job != null) { job?.cancel() job = null } if (executorService == null || executorService?.isShutdown == true) { executorService = Executors.newFixedThreadPool(1) executorService?.execute { runBlocking { job = launch(Dispatchers.IO) { candlesState.combine( studiesListState, transform = { candles, studies -> Pair(candles, studies) } ).combine( sessionsFlow, transform = { pair, sessions -> Pair(pair, sessions) } ).collect { pair -> val candles = pair.first.first val settings = pair.first.second val sessions = pair.second try { if (candles.candlesData.isEmpty()) { updateDxStudies(0, listOf()) } else { updateDxStudies(candles.candlesData.size, candles.candlesData) } if (candles.aggregation.isDayOrMore()) { dxStudies.setTradingSessions( candles.candlesData .zipWithNext() .map { (candle, nextCandle) -> object : TradingSessionData { override val from: Double = candle.timestamp.toDouble() override val high: Double? = null override val low: Double? = null override val to: Double = nextCandle.timestamp.toDouble() } }.toTypedArray() ) } else {dxStudies.setTradingSessions( sessions.map { session -> object : TradingSessionData { override val from: Double = session.from override val to: Double = session.to override val high: Double? = session.high override val low: Double? = session.low } }.toTypedArray() )} val studies = settings.map { studiesSetting -> val params = getParams(studiesSetting).toTypedArray() dxStudies.createStudy(studiesSetting.id, params) }.mapIndexed { index, study -> StudiesData(settings[index].uuid, study.calculateAll()) } _dataFlow.emit(studies) } catch (e: Exception) { _errorFlow.tryEmit(StudiesProviderError.UnknownError("Failed to process studies data: $e.message", e)) } } } } } } else { Log.w(TAG, "ExecutorService is already running.") } } /** * Updates the [dxStudies] with the given size and candle data. * * @param size The size of the studies. * @param candles The list of candle data [CandleDO]. */ private fun updateDxStudies(size: Int, candles: List) { dxStudies = DxStudies( size, candles.map { it.toStudiesCandle() }.toTypedArray() ) } /** * Converts studies configuration parameters to a list of [StudyParam]. * * @param studiesConfig The studies configuration. * @return List of [StudyParam] representing the configuration parameters. */ private fun getParams(studiesConfig: StudiesSetting): List { return studiesConfig.parameters.map { it.toStudyParam() } } /** * Stops the background job and shuts down the executor service. */ fun disconnect() { job?.cancel() job = null executorService?.shutdown() executorService = null } /** * Sets the DX chart candles for processing. * * @param candles The [DxChartsCandles]. */ override fun setCandles(candles: DxChartsCandles) { candlesState.tryEmit(candles) } /** * Sets the list of studies settings for processing. * * @param studies The list of [StudiesSetting]s. */ override fun setStudiesList(studies: List) { studiesListState.tryEmit(studies) } /** * Retrieves the list of studies settings of enabled indicators. * * @return List of [StudiesSetting]. */ override fun getEnabledStudies(): List { return studiesListState.value } /** * Updates the trading sessions for processing. * * @param sessions The list of [TradingSession]s. */ override fun updateTradingSessions(sessions: List) { sessionsFlow.tryEmit(sessions) } companion object{ private const val TAG = "DxStudiesDataProvider" } } /** * Sealed class representing different types of errors that might occur in the DxStudiesDataProviderImplementation. */ sealed class StudiesProviderError(override val message: String, override val error: Throwable?) : ProviderError { class UnknownError( override val message: String, override val error: Throwable? = null ) : StudiesProviderError(message, error) } `} --- ## Chart for Android/Configuration/Providers/Data, configuration and implementation of providers/Studies preview data Provider.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Studies preview data Provider Provider is designed to calculate indicator data on the preview screen and transfer the calculated data to the library. The indicator is calculated as a single instance. {` /** * Interface for receiving calculated study data for studies preview screen * * When calculating data for a preview quote, parameters selected by user are transferred to this interface, then the calculated study is received from here * * When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.studies.DxStudiesPreviewDataProviderImplementation] and pass it to the library using [DxChartsDataProviders] data class * * The [dataFlow] property is used to get calculated study data. It is a StateFlow that emits a [StudiesData] object representing the study data. * * The [setPreviewCandles] method is used to set candles data for study calculation. * * The [setPreviewStudySetting] method is used to set study configuration that has to be calculated. * * The [updateTradingSessions] method is used to update trading sessions for study. */ interface DxChartsStudiesPreviewDataProvider { /** * Flow of receiving calculated study data * * This property is a StateFlow that emits a [StudiesData] object. The [StudiesData] object represents the calculated study data for a particular study. */ val dataFlow: StateFlow /** * Sets candles data for study calculation * * This method is used to set the candles data that will be used for calculating the study. The candles data is represented by a [DxChartsCandles] object. * * @param candles The candles data. */ fun setPreviewCandles(candles: DxChartsCandles) /** * Sets study configuration that has to be calculated * * This method is used to set the study configuration that will be calculated. The configuration is represented by a [StudiesSetting] object. * * @param studySetting The study configuration. */ fun setPreviewStudySetting(studySetting: StudiesSetting) /** * Updates trading sessions for study calculators * * This method is used to update the trading sessions that will be used for calculating the study. Each trading session is represented by a [TradingSession] object. * * @param sessions The list of trading sessions. */ fun updateTradingSessions(sessions: List) } `} Methods of the interface: - `setPreviewCandles` - sets candles data for the indicator that should be calculated. - `setPreviewStudySetting` - sets the indicator settings for calculation. - `updateTradingSessions` - sets trading sessions data for indicator calculation. Data is sent by updating the state of the `dataFlow` variable. It is represented as `com.devexperts.dxcharts.provider.StudiesData` object. Incoming data is represented as `com.devexperts.dxcharts.provider.StudiesSetting` object. The preview of the indicator: indicators_bottom Here is the default implementation of **`DxChartsStudiesPreviewDataProvider`**: {` /** * Implementation of the DxChartsStudiesPreviewDataProvider interface, providing functionality * to manage preview studies data and candles for DX charts. * * @property _dataFlow Internal mutable state flow to emit preview studies data to the UI. * @property dataFlow Exposed immutable state flow to observe preview studies data from the UI. * @property previewCandlesState Internal mutable state flow to manage preview DX chart candles. * @property previewStudyState Internal mutable state flow to manage the preview studies setting. * @property sessionsFlow Internal mutable state flow to manage trading sessions for preview. * @property previewExecutorService Executor service for managing background tasks specific to previews. * @property previewJob Background job for executing preview data processing. * @property _errorFlow Internal [MutableStateFlow] for sending errors. * @property errorFlow [StateFlow] for sending errors. */ class DxStudiesPreviewDataProviderImplementation : DxChartsStudiesPreviewDataProvider, DxChartsErrorProvider { private val _dataFlow: MutableStateFlow = MutableStateFlow(null) private val previewCandlesState: MutableStateFlow = MutableStateFlow(DxChartsCandles()) private val previewStudyState: MutableStateFlow = MutableStateFlow(null) private val sessionsFlow: MutableStateFlow> = MutableStateFlow(emptyList()) private var previewExecutorService: ExecutorService? = null private var previewJob: Job? = null override val dataFlow: StateFlow get() = _dataFlow private val _errorFlow = MutableStateFlow(null) override val errorFlow: StateFlow get() = _errorFlow /** * Starts the background job for processing preview data. * If a previous job exists, it is canceled before starting a new one. * * Inside this method, a background job is executed using the [previewExecutorService]. * The job collects and combines data from [previewCandlesState], [previewStudyState], and [sessionsFlow] * using the [combine] operator. It then processes the combined data to create a preview version of DX studies. * Finally, it emits the preview studies data to the UI through [_dataFlow]. * * For details, please, check [DxStudiesDataProviderImplementation.connect] */ fun connect() { if (previewJob != null) { previewJob?.cancel() previewJob = null } if (previewExecutorService == null || previewExecutorService?.isShutdown == true) { previewExecutorService = Executors.newFixedThreadPool(1) previewExecutorService?.execute { runBlocking { previewJob = launch(Dispatchers.IO) { previewCandlesState.combine( previewStudyState, transform = { candles, study -> Pair(candles, study) } ).combine( sessionsFlow, transform = { pair, list -> Pair(pair, list) } ).collect { pair -> val candles = pair.first.first val studiesSetting = pair.first.second val sessions = pair.second val dxStudies = DxStudies( candles.candlesData.size, candles.candlesData.map { it.toStudiesCandle() }.toTypedArray() ) try { if (candles.aggregation.isDayOrMore()) { dxStudies.setTradingSessions( candles.candlesData .zipWithNext() .map { (candle, nextCandle) -> object : TradingSessionData { override val from: Double = candle.timestamp.toDouble() override val high: Double? = null override val low: Double? = null override val to: Double = nextCandle.timestamp.toDouble() } }.toTypedArray() ) } else { dxStudies.setTradingSessions( sessions.map { session -> object : TradingSessionData { override val from: Double = session.from override val to: Double = session.to override val high: Double? = session.high override val low: Double? = session.low } }.toTypedArray() ) } studiesSetting?.let { val params = getParams(it).toTypedArray() _dataFlow.emit( StudiesData( it.uuid, dxStudies.createStudy(it.id, params).calculateAll() ) ) } } catch (e: Exception) { _errorFlow.tryEmit(StudiesPreviewProviderError.UnknownError("Failed to process studies data: $e.message", e)) } } } } } } else { Log.w(TAG, "ExecutorService is already running.") } } /** * Stops the background job for processing preview data and shuts down the executor service. */ fun disconnect() { previewJob?.cancel() previewJob = null previewExecutorService?.shutdown() previewExecutorService = null } /** * Sets the preview DX chart candles for processing. * * @param candles The preview DX chart candles. */ override fun setPreviewCandles(candles: DxChartsCandles) { previewCandlesState.tryEmit(candles) } /** * Sets the preview studies setting for processing. * * @param studySetting The preview studies setting. */ override fun setPreviewStudySetting(studySetting: StudiesSetting) { previewStudyState.tryEmit(studySetting) } /** * Converts studies configuration parameters to a list of [StudyParam]. * * @param studiesConfig The studies configuration. * @return List of [StudyParam] representing the configuration parameters. */ private fun getParams(studiesConfig: StudiesSetting): List { return studiesConfig.parameters.map { it.toStudyParam() } } /** * Retrieves the list of preview trading sessions for processing. * * @param sessions The list of preview trading sessions. */ override fun updateTradingSessions(sessions: List) { sessionsFlow.tryEmit(sessions) } companion object { private const val TAG = "DxStudiesPreviewDataProvider" } } /** * Sealed class representing different types of errors that might occur in the DxStudiesDataProviderImplementation. */ sealed class StudiesPreviewProviderError(override val message: String, override val error: Throwable?) : ProviderError { class UnknownError( override val message: String, override val error: Throwable? = null ) : StudiesPreviewProviderError(message, error) } `} --- ## Chart for Android/Configuration/Providers/Overview.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Providers To operate, the DxChart library requires obtaining data from external sources: 1. Candle data 2. List of instruments 3. Schedule og the instrument trading hours 4. Quotes 5. Events about an instrument 6. News about an instrument 7. Indicators To obtain these, the following interfaces have been implemented, whose purpose is to supply data to the library: - `com.devexperts.dxcharts.provider.DxChartsCandlesProvider` - provides data about candles for the selected instrument - `com.devexperts.dxcharts.provider.DxChartsEventsProvider` - provides data about events for the selected instrument (dividends, corporate calendar, splits) - `com.devexperts.dxcharts.provider.DxChartsIPFProvider` - provides data about trading instruments (type, name, exchange time zone) - `com.devexperts.dxcharts.provider.DxChartsScheduleProvider` - provides schedule of the instrument trading hours - `com.devexperts.dxcharts.provider.DxChartsNewsProvider` - provides data about news related to the selected trading instrument - `com.devexperts.dxcharts.provider.DxChartsQuotesProvider` - provides quotes for the selected trading instrument - `com.devexperts.dxcharts.provider.DxChartsStudiesDataProvider` - calculates and provides data about indicators on the chart - `com.devexperts.dxcharts.provider.DxChartsStudiesPreviewDataProvider` - calculates and provides data about the indicator selected for preview during editing Library users can independently implement interfaces to fetch data from their sources. All providers interfaces are located in the package `com.devexperts.dxcharts.provider`. Passing providers into the library is done using the `com.devexperts.dxcharts.provider.DxChartsDataProviders` class: {` data class DxChartsDataProviders( val candlesProvider: DxChartsCandlesProvider? = null, val quotesProvider: DxChartsQuotesProvider? = null, val ipfProvider: DxChartsIPFProvider? = null, val scheduleProvider: DxChartsScheduleProvider? = null, val eventsProvider: DxChartsEventsProvider? = null, val newsProvider: DxChartsNewsProvider? = null, val studiesDataProvider: DxChartsStudiesDataProvider? = null, val studiesDataPreviewProvider: DxChartsStudiesPreviewDataProvider? = null, ) `} Which is then passed into `com.devexperts.dxcharts.lib.domain.DxChartsConfig` and `com.devexperts.dxcharts.lib.ui.DxChartsScreen`: {` //... val dxChartsDataProviders = DxChartsDataProviders( /* providers initialization */ ) setContent { DxChartsTheme(/* Theme params*/ ) { DxChartsScreen( dxChartsConfig = DxChartsConfig( providers = dxChartsDataProviders, //... ) ) } } //... `} By default, all providers are set to null, and no data is received in the library. Each provider follows a similar template, let's consider the example of `DxChartsQuotesProvider`: {` /** * Interface for receiving quotes data * * When loading dxCharts library chart, quotes data is taken from this interface * * When connecting dxCharts library, developer can implement this interface or use the default implementation [com.devexperts.dxcharts.provider.quotes.DxFeedQuotesProvider] and pass it to the library using [DxChartsDataProviders] data class * * Use [dataFlow] to get quotes data * interface DxChartsQuotesProvider { /** * Flow of receiving quotes data * * Quotes data is represented by [Quote] */ val dataFlow: StateFlow /** * Changes quotes' instrument symbol * * @param symbol instrument symbol */ fun changeSymbol(symbol: String) } `} - The received data is passed into a `StateFlow`, to which internal modules of the library subscribe. - There is a method for changing the trading instrument symbol to fetch new data. - The providers operate asynchronously to the main logic of the library. ## Default Providers For DXfeed clients we already have default implementations of providers that can be easily used for our library. Default providers: {` /** * [endpointAddress] connects to the dxFeed API at the [endpointAddress]. * [url] parameter is the base URL for accessing the dxFeed API. * [token] parameter is used for authorization with Base64 encoding. */ class DxFeedCandlesProvider(val endpointAddress: String) class DxFeedQuotesProvider(val endpointAddress: String = "") class DxFeedNewsProvider(val url: String = "", val token: String = "") class DxFeedHttpIpfProvider(val url: String = "", val token: String = "") class DxFeedHttpScheduleProvider(val token: String = "") class DxFeedEventsProvider(val url: String = "", val token: String = "") class DxStudiesDataProviderImplementation class DxStudiesPreviewDataProviderImplementation `} `DxFeedCandlesProvider` and `DxFeedQuotesProvider` are classes used to connect to the dxFeed data service in order to receive market data such as candle (OHLC) information and quotes. Both `DxFeedCandlesProvider` and `DxFeedQuotesProvider` require an endpointAddress parameter, which specifies the address of the dxFeed server or endpoint that these providers will connect to. {` DXEndpoint.getInstance().connect(endpointAddress) `} `DXEndpoint.getInstance()`: This retrieves the singleton instance of `DXEndpoint`, which is responsible for managing the connection to dxFeed services. `connect(endpointAddress)`: This method establishes a network connection to the dxFeed server using the provided `endpointAddress` In the `DxFeedNewsProvider`, `DxFeedHttpIpfProvider`, and `DxFeedEventsProvider` classes, the OkHttp library is used to perform HTTP requests in order to retrieve data from a specified `url`. {` private val request = Request.Builder() .url(url) .addHeader("Authorization", token) .build() `} `.url(url)`: The `.url()` method sets the target `URL` that the HTTP request will be sent to. The url variable is expected to contain a string representing the endpoint from which the data will be retrieved (e.g. `https://api.dxfeed.com/news`). `.addHeader("Authorization", token)`: The `token` variable contains the actual token used for authentication or authorization purposes. Example of how we create an instance of default provider in our demo application: {` private val newsProvider = DxFeedNewsProvider( url = "https://your.url.com/news", token = "Basic " + String( Base64.encode( "your:token".encodeToByteArray(), Base64.NO_WRAP ) ) ) `} `DxStudiesDataProviderImplementation` and `DxStudiesPreviewDataProviderImplementation` don't need any parameters. They use `dxstudies` library under the hood for calculating technical indicators. ### Error handling in the default providers Our default chart providers implement the `DxChartsErrorProvider` interface, which is designed to streamline error handling in your application. This interface allows you to subscribe to an `errorFlow`, a reactive stream that notifies you of any errors occurring in the provider. By subscribing to `errorFlow`, you can easily monitor and respond to errors in a centralized manner. This setup helps ensure that errors are managed effectively and can be handled appropriately based on your application's needs. Here’s a detailed overview of how the `DxChartsErrorProvider` interface works: - Error Flow: The `errorFlow` property provides a StateFlow that emits error events. You will receive updates through this flow whenever an error occurs. If there are no current errors, the flow will emit null, indicating that everything is operating normally. - Error Type: The type parameter T in `DxChartsErrorProvider` must extend `ProviderError`. This ensures that every error has a descriptive message, which is crucial for understanding the nature of the error and for debugging purposes. - ProviderError Interface: This interface defines the structure for errors that include a message and error itself. Implementing `ProviderError` allows you to attach detailed descriptions to errors, which can be used for logging or displaying informative messages to users. {` /** * Interface for providing a state flow for errors. * * This interface defines a contract for providers that need to expose a stream of errors via a [StateFlow]. * Implementations of this interface will provide a [StateFlow] that emits errors of type [T]. * * The type parameter T should extend ProviderError to ensure that all errors have a message property. * * @param T Type of the error. This type must extend [ProviderError] to include a message property. */ interface DxChartsErrorProvider { /** * A [StateFlow] that emits errors of type [T]. * * Subscribers to this flow will receive updates whenever an error of type [T] occurs. * The flow will emit null if there are no current errors, allowing subscribers to handle the absence of errors. * * @return A [StateFlow] emitting errors of type [T]. */ val errorFlow: StateFlow } /** * Represents an error with an associated message and an optional underlying exception. * * Implement this interface to provide errors that include a description and, if available, * an underlying exception that caused the error. This is useful for logging, debugging, or * user notifications where additional context about the error is needed. */ interface ProviderError { /** * A descriptive message about the error. * * This message provides context for the error and can be used for both logging purposes * and informing users about what went wrong. * * @return The error message. */ val message: String /** * An optional underlying exception that caused this error. * * If an exception is available, it provides more detailed information about the cause of * the error. This can be useful for debugging and error handling. * * @return The underlying [Exception] if available, or null if there is no additional * exception information. */ val error: Throwable? } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Aggregations Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Aggregations Repository - Stores the default chart aggregation - Stores the user's list of aggregations - The interface for the repository is `AggregationsRepository`. The repository is mandatory for the library to function and is passed to `com.devexperts.dxcharts.lib.domain.DxChartsConfig`. The standard implementation is `com.devexperts.dxcharts.lib.data.implementation.DefaultAggregationsRepository`. AggregationRepository Interface: {` /** * Interface for the repository to store aggregation data. * * Aggregation is used when retrieving candle data from the corresponding provider. * * On the first run of the library, [getSelectedAggregation] should return the default aggregation. * * Implementation of this interface is mandatory for the library to function. It is passed to [com.devexperts.dxcharts.lib.domain.DxChartsConfig]. * * The standard implementation is [com.devexperts.dxcharts.lib.data.repo.default_repos.DefaultAggregationsRepository]. * * @see Aggregation */ interface AggregationsRepository { /** * Adds a new aggregation to the list of available aggregations for selection. */ fun addAggregation(aggregation: Aggregation) /** * Sets the selected aggregation. */ fun setSelectedAggregation(aggregation: Aggregation) /** * Returns a list of all aggregations for display on the aggregation selection screen. * * @return List of all aggregations */ fun getAggregations(): List /** * Removes an aggregation from the list of available aggregations for selection. */ fun removeAggregation(aggregation: Aggregation) /** * Returns the currently selected or default aggregation. * * @return Selected aggregation */ fun getSelectedAggregation(): Aggregation } /*****/ data class Aggregation( val value: Int, val multiplier: TimeUnit = TimeUnit.SECONDS, ) `} --- ## Chart for Android/Configuration/Repositories/Implementation/Colors Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Colors Repository - Stores the list of colors for the color palette. - Stores the global color for new drivings. - `androidx.compose.ui.graphics.Color` class is used to store the color. - In the DxCharts library, it is passed in two instances: - Repository for colors for the settings screen, studies configuration screen, all drivings except the Highlighter drawing. - Repository for colors for the Highlighter drawing. {` data class Repositories( //... val colorsRepository: ColorsRepository = LocalStorageDefaultColorsRepository(), val highlighterColorsRepository: ColorsRepository = LocalStorageDefaultHighlighterColorsRepository(), //... ) `} Repository interface: {` /** * Interface for the repository to store colors data from the chart palettes. * * The methods [getActiveColor] and [setActiveColor] are used in the palette for drawings. * * The method [getActiveColor] should return the default color for drawings if another didn't selected. * * Other methods are used in palettes for settings items, indicators, and drawings. * * Implementation of this interface is mandatory for the library to function. It is passed to [com.devexperts.dxcharts.lib.domain.DxChartsConfig]. * * The standard implementation is [com.devexperts.dxcharts.lib.data.repo.default_repos.DefaultColorsRepository]. */ interface ColorsRepository { /** * Returns the currently selected or default color for drawings. */ fun getActiveColor(): Color /** * Sets the selected color for drawings. */ fun setActiveColor(color: Color) /** * Adds a new color to the list of available colors for selection. */ fun addColor(color: Color) /** * Removes a color from the list of available colors for selection. */ fun removeColor(color: Color) /** * Returns a list of all colors for display in the palette. * * @return List of all colors */ fun getColors(): List } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Drawings Repository/DefaultDrawingsLineWidthRepo.mdx import { MDXCodeBlock } from '../../../../../../mdx-config/code-block/MDXCodeBlock'; # DefaultDrawingsLineWidthRepo Stores information about the current global line width / text size value for new drawings, provides data about the minimum (minLineWidth) and maximum (maxLineWidth) line length / text size. Default implementation: {` /** * Default implementation of [com.devexperts.dxcharts.lib.data.repo.DrawingsLineWidthRepo]. * * This class provides a default implementation of the [DrawingsLineWidthRepo] interface. * * @property getLineWidthCallback Callback function to get the line width for new drawings. * @property setLineWidthCallback Callback function to set the line width for new drawings. * @property minLineWidth Minimum line width. Default value is 1. * @property maxLineWidth Maximum line width. Default value is 5. * * @see DrawingsLineWidthRepo */ open class DefaultDrawingsLineWidthRepo( private val getLineWidthCallback: () -> Int, private val setLineWidthCallback: (Int) -> Unit, override val minLineWidth: Int = MIN_LINE_WIDTH, override val maxLineWidth: Int = MAX_LINE_WIDTH ) : DrawingsLineWidthRepo { /** * Method to get the line width for new drawings. * * @return Line width for new drawings. */ override fun getLineWidth(): Int = getLineWidthCallback() /** * Method to set the line width for new drawings. * * @param px The line width in pixels. */ override fun setLineWidth(px: Int) { setLineWidthCallback(px) } /** * Default line width for new drawings when the library is launched. */ companion object { const val DEFAULT_LINE_WIDTH = 1 const val MIN_LINE_WIDTH = 1 const val MAX_LINE_WIDTH = 5 } } `} Implementation of the default repository for local storage: {` /** * Local storage implementation of [DrawingsLineWidthRepo]. * * This class provides a local storage implementation of the [DrawingsLineWidthRepo] interface, * using a default line width and storing it locally. * * @constructor Creates a LocalStorageDefaultDrawingsLineWidthRepo. */ class LocalStorageDefaultDrawingsLineWidthRepo : DrawingsLineWidthRepo { /** * The line width stored locally. */ private var lineWidth = DefaultDrawingsLineWidthRepo.DEFAULT_LINE_WIDTH /** * The default repository instance using the local line width. */ private val defaultRepo = DefaultDrawingsLineWidthRepo( getLineWidthCallback = ::lineWidth, setLineWidthCallback = ::lineWidth::set, ) /** * The minimum line width. */ override val minLineWidth: Int = 1 /** * The maximum line width. */ override val maxLineWidth: Int = 5 /** * Method to get the line width. * * @return The line width. */ override fun getLineWidth(): Int { return defaultRepo.getLineWidth() } /** * Method to set the line width. * * @param px The line width in pixels. */ override fun setLineWidth(px: Int) { defaultRepo.setLineWidth(px) } } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Drawings Repository/DefaultDrawingsSnapshotsRepo.mdx import { MDXCodeBlock } from '../../../../../../mdx-config/code-block/MDXCodeBlock'; # DefaultDrawingsSnapshotsRepo - Stores snapshots (undo and redo steps) of drawings as scripts. A snapshot is saved when changing the color of a drawing, moving its point, changing the line width, or changing the font size. - Snapshots are parsed and placed in the `com.devexperts.dxcharts.lib.domain.drawings.AllDrawingsSnapshot` class. - The repository includes methods for: - adding a new snapshot - `addSnapshot` - checking for the existence of a previous snapshot - `hasPreviousStep` - checking for the existence of a next snapshot - `hasNextStep` - going to the previous snapshot - `previousStep` - going to the next snapshot - `nextStep` - checking for the presence of at least one snapshot - `canClear` - returning the current version of the set snapshot - `currentVersion` Default implementation of the repository: {` /** * Default implementation of [DrawingsSnapshotsRepo]. * * This class provides a default implementation of the [DrawingsSnapshotsRepo] interface. * * @property snapshots List of all drawing snapshots. * @property getMaxVersion Callback function to get the maximum snapshot index. * @property setMaxVersion Callback function to set the maximum snapshot index. * @property getCurrentVersionCallback Callback function to get the current snapshot index. * @property incrementAndGetCurrentVersion Callback function to increment and get the current snapshot index. * @property decrementAndGetCurrentVersion Callback function to decrement and get the current snapshot index. */ open class DefaultDrawingsSnapshotsRepo( private val snapshots: MutableList, @get:Synchronized private val getMaxVersion: () -> Int, @get:Synchronized private val setMaxVersion: (Int) -> Unit, @get:Synchronized private val getCurrentVersionCallback: () -> Int, @get:Synchronized private val incrementAndGetCurrentVersion: () -> Int, @get:Synchronized private val decrementAndGetCurrentVersion: () -> Int, ) : DrawingsSnapshotsRepo { /** * Initializes the repository with an empty snapshot. */ init { snapshots.add(0, AllDrawingsSnapshot.Empty) } /** * Method to add a new snapshot. * * When adding a new snapshot [snapshot], if it differs from the last emitted: * - the drawing is added to the [snapshots] list, * - the current version variable is incremented, * - the maximum version variable is set to the current version. * * Accordingly, if a snapshot with index 3 was set, with a maximum version of 7, then when adding a new snapshot versions 4, 5, 6, 7 will be erased, * and a new snapshot with version 4 is recorded instead, current = 4, max = 4. * * @param snapshot The snapshot to add. */ @Synchronized override fun addSnapshot(snapshot: AllDrawingsSnapshot) { val newList = snapshot.drawingsList val oldList = snapshots[getCurrentVersionCallback()].drawingsList if (!newList.cmp(oldList)) { snapshots.add(incrementAndGetCurrentVersion(), snapshot) setMaxVersion(getCurrentVersionCallback()) } } /** * Returns true if the version of the snapshot is not equal to 0 - it is possible to go back to the previous snapshot, otherwise false. * * @return True if the previous step is available, otherwise false. */ @Synchronized override fun hasPreviousStep(): Boolean { return getCurrentVersionCallback() != 0 } /** * Returns true if the version of the snapshot is not equal to the maximum - it is possible to move to the next snapshot, otherwise false. * * @return True if the next step is available, otherwise false. */ @Synchronized override fun hasNextStep(): Boolean { return getCurrentVersionCallback() != getMaxVersion() } /** * Returns true if the current snapshot is not empty - it is possible to clear all drawings, otherwise false. * * @return True if clear is possible, otherwise false. */ @Synchronized override fun canClear(): Boolean { return snapshots[getCurrentVersionCallback()] != AllDrawingsSnapshot.Empty } /** * Sets the previous snapshot as the current one and returns it. * * If the current snapshot is the first one, returns null. * * @return Previous snapshot or null. */ @Synchronized override fun previousStep(): AllDrawingsSnapshot? { return if (getCurrentVersionCallback() != 0) { snapshots[decrementAndGetCurrentVersion()] } else { null } } /** * Sets the next snapshot as the current one and returns it. * * If the current snapshot is the last one, returns null. * * @return Next snapshot or null. */ @Synchronized override fun nextStep(): AllDrawingsSnapshot? { return if (getCurrentVersionCallback() != getMaxVersion()) { snapshots[incrementAndGetCurrentVersion()] } else { null } } /** * Returns the version of the current snapshot. * * @return The version of the current snapshot. */ @Synchronized override fun currentVersion(): Int { return getCurrentVersionCallback() } } /** * Method for comparing two lists of drawings based on properties: * - id, * - keyPoints, * - type, * - properties.line.lineWidth, * - properties.text.textSize, * - properties.line.lineColor, * - properties.background.fillStyle, * - properties.text.textBg, * - properties.textValue * as well as their size. * * @return True if the lists are equal, otherwise false. */ private fun DrawingsList.cmp(other: DrawingsList): Boolean { val list1 = this.list val list2 = other.list if (list1 == null || list2 == null) { return list1 == null && list2 == null } if (list1.size != list2.size) { return false } list1.forEachIndexed { index, drawingObject -> val equals = (drawingObject.id == list2[index].id && drawingObject.keyPoints == list2[index].keyPoints && drawingObject.type == list2[index].type && drawingObject.properties?.line?.lineWidth == list2[index].properties?.line?.lineWidth && drawingObject.properties?.text?.textSize == list2[index].properties?.text?.textSize && drawingObject.properties?.line?.lineColor == list2[index].properties?.line?.lineColor && drawingObject.properties?.background?.fillStyle == list2[index].properties?.background?.fillStyle && drawingObject.properties?.text?.textBg == list2[index].properties?.text?.textBg && drawingObject.properties?.textValue == list2[index].properties?.textValue) if (!equals) { return false } } return true } `} Implementation of the default repository for local storage: {` /** * Local storage implementation of [DrawingsSnapshotsRepo]. * * This class provides a local storage implementation of the [DrawingsSnapshotsRepo] interface, * storing drawing snapshots locally with support for navigation and clearing. * * @constructor Creates a LocalStorageDefaultDrawingsSnapshotsRepo. */ class LocalStorageDefaultDrawingsSnapshotsRepo : DrawingsSnapshotsRepo { /** * The maximum snapshot version. */ private var maxVersion: Int = 0 /** * The current snapshot version. */ private val currentVersion = AtomicInteger(0) /** * The list of all drawing snapshots. */ private val snapshots = ArrayList() /** * The default repository instance using local storage. */ private val defaultRepo = DefaultDrawingsSnapshotsRepo( getMaxVersion = ::maxVersion, setMaxVersion = ::maxVersion::set, getCurrentVersionCallback = currentVersion::get, incrementAndGetCurrentVersion = currentVersion::incrementAndGet, decrementAndGetCurrentVersion = currentVersion::decrementAndGet, snapshots = snapshots ) /** * Method to add a new snapshot. * * @param snapshot The snapshot to add. */ override fun addSnapshot(snapshot: AllDrawingsSnapshot) { defaultRepo.addSnapshot(snapshot) } /** * Checks if there is a previous step available. * * @return True if a previous step is available, otherwise false. */ override fun hasPreviousStep(): Boolean { return defaultRepo.hasPreviousStep() } /** * Checks if there is a next step available. * * @return True if a next step is available, otherwise false. */ override fun hasNextStep(): Boolean { return defaultRepo.hasNextStep() } /** * Checks if clearing is possible. * * @return True if clearing is possible, otherwise false. */ override fun canClear(): Boolean { return defaultRepo.canClear() } /** * Moves to the previous step and returns the corresponding snapshot. * * @return The previous snapshot, or null if not available. */ override fun previousStep(): AllDrawingsSnapshot? { return defaultRepo.previousStep() } /** * Moves to the next step and returns the corresponding snapshot. * * @return The next snapshot, or null if not available. */ override fun nextStep(): AllDrawingsSnapshot? { return defaultRepo.nextStep() } /** * Retrieves the version of the current snapshot. * * @return The version of the current snapshot. */ override fun currentVersion(): Int { return defaultRepo.currentVersion() } } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Drawings Repository/DefaultDrawingsTypesRepo.mdx import { MDXCodeBlock } from '../../../../../../mdx-config/code-block/MDXCodeBlock'; # DefaultDrawingsTypesRepo Stores information about the drawings selected by the user. Default implementation: {` /** * Default implementation of [DrawingsTypesRepo]. * * This class provides a default implementation of the [DrawingsTypesRepo] interface, * allowing the management of drawing types with no persistence across application restarts. * * @property selectedDrawingsTypes The collection of selected drawing types. * @constructor Creates a DefaultDrawingsTypesRepo with the specified selected drawing types. */ open class DefaultDrawingsTypesRepo( private val selectedDrawingsTypes: MutableCollection ) : DrawingsTypesRepo { /** * Method to add a drawing type to the list of selected drawing types. * * @param drawingType The drawing type to select. */ override fun selectDrawingType(drawingType: DrawingType) { selectedDrawingsTypes.add(drawingType) } /** * Method to remove a drawing type from the list of selected drawing types. * * @param drawingType The drawing type to deselect. */ override fun deselectDrawingType(drawingType: DrawingType) { selectedDrawingsTypes.remove(drawingType) } /** * Method to get the list of selected drawing types. * * @return The list of selected drawing types. */ override fun getSelected(): List = selectedDrawingsTypes.toList() } `} Implementation of the default repository for local storage: {` /** * Local storage implementation of [DrawingsTypesRepo]. * * This class provides a local storage implementation of the [DrawingsTypesRepo] interface, * managing the selection of drawing types with local storage. * * @constructor Creates a LocalStorageDefaultDrawingsTypesRepo. */ class LocalStorageDefaultDrawingsTypesRepo : DrawingsTypesRepo { /** * Default set of drawing types. */ private val defaultValues = setOf( DrawingType.TREND_LINE, DrawingType.PRICE_LINE, DrawingType.TIME_LINE, DrawingType.TEXT, ) /** * Collection of selected drawing types. */ private val selectedDrawingsTypes: HashSet = HashSet(defaultValues) /** * The default repository instance using local storage. */ private val defaultRepo = DefaultDrawingsTypesRepo( selectedDrawingsTypes = selectedDrawingsTypes, ) /** * Method to select a drawing type. * * @param drawingType The drawing type to select. */ override fun selectDrawingType(drawingType: DrawingType) { defaultRepo.selectDrawingType(drawingType) } /** * Method to deselect a drawing type. * * @param drawingType The drawing type to deselect. */ override fun deselectDrawingType(drawingType: DrawingType) { defaultRepo.deselectDrawingType(drawingType) } /** * Method to get the selected drawing types. * * @return The collection of selected drawing types. */ override fun getSelected(): Collection { return defaultRepo.getSelected() } } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Drawings Repository/DefaultFirstClickRepo.mdx import { MDXCodeBlock } from '../../../../../../mdx-config/code-block/MDXCodeBlock'; # DefaultFirstClickRepo Stores information about the first clicks on the 'Magnet' and 'Brush' buttons, as well as whether a text drawing (Text or Callout) was set for the first time. Default implementation of the repository: {` /** * Default implementation of [DrawingsFirstClickRepo]. * * This class provides a default implementation of the [DrawingsFirstClickRepo] interface, * managing the first click actions in the drawing toolbar with no persistence across application restarts. * * @property isMagnetModeClicked Indicates whether the "Magnet" button on the drawing toolbar has been clicked. Default is false. * @property isDrawingModeClicked Indicates whether the "Brush" button on the drawing toolbar has been clicked. Default is false. * @property isDrawingTextShowed Indicates whether a text drawing has been shown. Default is false. * @property setMagnetModeClicked Callback function to set the "Magnet" button clicked status. * @property setDrawingModeClicked Callback function to set the "Brush" button clicked status. * @property setDrawingTextShowed Callback function to set the text drawing status. * @constructor Creates a DefaultFirstClickRepo with the specified callbacks. */ open class DefaultFirstClickRepo( private val isMagnetModeClicked: () -> Boolean, private val isDrawingModeClicked: () -> Boolean, private val isDrawingTextShowed: () -> Boolean, private val setMagnetModeClicked: (Boolean) -> Unit, private val setDrawingModeClicked: (Boolean) -> Unit, private val setDrawingTextShowed: (Boolean) -> Unit, ) : DrawingsFirstClickRepo { /** * Method called when the "Magnet" button on the drawing toolbar is clicked. * Returns true if called for the first time, otherwise returns false. * * @return True if the "Magnet" button is clicked for the first time, false otherwise. */ override fun magnetModeClicked(): Boolean { return if (!isMagnetModeClicked()) { setMagnetModeClicked(true) true } else { false } } /** * Method called when the "Brush" button on the drawing toolbar for drawing is clicked. * Returns true if called for the first time, otherwise returns false. * * @return True if the "Brush" button is clicked for the first time, false otherwise. */ override fun drawingModeClicked(): Boolean { return if (!isDrawingModeClicked()) { setDrawingModeClicked(true) true } else { false } } /** * Method called when a text drawing is set. * Returns true if called for the first time, otherwise returns false. * * @return True if a text drawing is shown for the first time, false otherwise. */ override fun drawingTextShowed(): Boolean { return if (!isDrawingTextShowed()) { setDrawingTextShowed(true) true } else { false } } } `} Implementation of the default repository for local storage: {` /** * Local storage-based implementation of [DrawingsFirstClickRepo]. * * This class provides a local storage-based implementation of the [DrawingsFirstClickRepo] interface, * managing the first click actions in the drawing toolbar with no persistence across application restarts. * * @constructor Creates a LocalStorageDefaultFirstClickRepo. */ class LocalStorageDefaultFirstClickRepo : DrawingsFirstClickRepo { /** * Indicates whether the "Magnet" button on the drawing toolbar has been clicked. */ private var magnetModeClicked = false /** * Indicates whether the "Brush" button on the drawing toolbar has been clicked. */ private var drawingModeClicked = false /** * Indicates whether a text drawing has been shown. */ private var drawingTextClicked = false /** * The default repository instance using local storage. */ private val defaultRepo = DefaultFirstClickRepo( isMagnetModeClicked = { magnetModeClicked }, isDrawingModeClicked = { drawingModeClicked }, isDrawingTextShowed = { drawingTextClicked }, setMagnetModeClicked = { magnetModeClicked = it }, setDrawingModeClicked = { drawingModeClicked = it }, setDrawingTextShowed = { drawingTextClicked = it } ) /** * Method called when the "Magnet" button on the drawing toolbar is clicked. * Returns true if called for the first time, otherwise returns false. * * @return True if the "Magnet" button is clicked for the first time, false otherwise. */ override fun magnetModeClicked(): Boolean { return defaultRepo.magnetModeClicked() } /** * Method called when the "Brush" button on the drawing toolbar for drawing is clicked. * Returns true if called for the first time, otherwise returns false. * * @return True if the "Brush" button is clicked for the first time, false otherwise. */ override fun drawingModeClicked(): Boolean { return defaultRepo.drawingModeClicked() } /** * Method called when a text drawing is set. * Returns true if called for the first time, otherwise returns false. * * @return True if a text drawing is shown for the first time, false otherwise. */ override fun drawingTextShowed(): Boolean { return defaultRepo.drawingTextShowed() } } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Drawings Repository/Drawings Repository.mdx import { MDXCodeBlock } from '../../../../../../mdx-config/code-block/MDXCodeBlock'; # Drawings Repository - Stores data for all library logic related to drawings. - Consists of 4 components: - DrawingsLineWidthRepo - selected line thickness for drawings - TypesRepo - selected drawings from the general list displayed in the corresponding toolbar - SnapshotsRepo - snapshots of drawings created on the chart - FirstClickRepo - initial press of "Magnet" and "Brush" buttons on the drawing toolbar - a hint should be shown on the first press, and it also stores whether the hint was shown after setting a text drawing Repository interfaces: {` /** * Interface for storing data about the first press of "Magnet" and "Brush" buttons on the drawing toolbar. * It also stores data about whether the Hint was shown after setting a text drawing. * * Implementation of this interface is mandatory for the library to function. * * Default implementation - [com.devexperts.dxcharts.lib.data.repo.default_repos.DefaultFirstClickRepo] */ interface DrawingsFirstClickRepo { /** * Method called when the "Magnet" button on the drawing toolbar is pressed. * Returns true if the method was called for the first time, false otherwise. */ fun magnetModeClicked(): Boolean /** * Method called when the "Brush" button on the drawing toolbar is pressed. * Returns true if the method was called for the first time, false otherwise. */ fun drawingModeClicked(): Boolean /** * Method called when a text drawing is set. * Returns true if the method was called for the first time, false otherwise. */ fun drawingTextShowed(): Boolean } /** * Interface for storing data about the selected line width of drawings. * * Implementation of this interface is mandatory for the library to function. * * Default implementation - [com.devexperts.dxcharts.lib.data.repo.DrawingsLineWidthRepo] * * @property minLineWidth Minimum line width * @property maxLineWidth Maximum line width */ interface DrawingsLineWidthRepo { val minLineWidth: Int val maxLineWidth: Int /** * Method returns the currently set line width. */ fun getLineWidth(): Int /** * Method sets the current line width. * * @param px Line width in pixels */ fun setLineWidth(px: Int) } /** * Interface for storing data about the selected drawings from the common list, displayed in the corresponding toolbar. * * Implementation of this interface is mandatory for the library to function. * * Default implementation - [com.devexperts.dxcharts.lib.data.repo.DrawingsTypesRepo] */ interface DrawingsTypesRepo { /** * Method for selecting a drawing from the common list of drawings and adding it to the toolbar. * * @param drawingType Drawing to be added to the toolbar */ fun selectDrawingType(drawingType: DrawingType) /** * Method for removing a drawing from the toolbar. * * @param drawingType Drawing to be removed from the toolbar */ fun deselectDrawingType(drawingType: DrawingType) /** * Method to get a list of selected drawings. */ fun getSelected(): Collection } /** * Interface for storing data about snapshots of drawings that have been drawn on the chart. * * Implementation of this interface is mandatory for the library to function. * * Default implementation - [com.devexperts.dxcharts.lib.data.repo.DrawingsSnapshotsRepo] */ interface DrawingsSnapshotsRepo { /** * Adds a new snapshot to the repository. */ fun addSnapshot(snapshot: AllDrawingsSnapshot) /** * Returns true if there is a snapshot before the current one, otherwise false. */ fun hasPreviousStep(): Boolean /** * Returns true if there is a snapshot after the current one, otherwise false. */ fun hasNextStep(): Boolean /** * Returns true if it is possible to clear the chart of drawings (current snapshot is not empty), otherwise false. */ fun canClear(): Boolean /** * Sets and returns the previous snapshot relative to the current one. */ fun previousStep(): AllDrawingsSnapshot? /** * Sets and returns the next snapshot relative to the current one. */ fun nextStep(): AllDrawingsSnapshot? /** * Returns the version of the current snapshot. */ fun currentVersion(): Int } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Instrument Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Instrument Repository Stores information about the currently selected instrument. The instrument is represented as an object of the class `com.devexperts.dxcharts.provider.domain.InstrumentData`. Repository interface: {` interface InstrumentRepository { /** * Method to set the selected instrument. */ fun setSelectedInstrument(instrument: InstrumentData) /** * Method to get the selected instrument. */ fun getSelectedInstrument(): InstrumentData } /****/ /** * Data class for storing data about instruments * * @param type type of instrument (f.e. STOCK, CRYPTO) * @param symbol symbol of instrument (f.e. GOOG, TSLA, AAPL) * @param description description of instrument (f.e. Alphabet Inc. - Class C Capital Stock) * @param country short name of a country of the instrument (f.e. US) * @param currency short name of a currency of the instrument (f.e. USD) * @param priceIncrements minimal price increments of the instrument (f.e. 0.01) * @param schedule parsed trading hours of the instrument */ data class InstrumentData( val type: String, val symbol: String, val description: String, val country: String, val currency: String, val priceIncrements: String, val schedule: Schedule? = null, ) { companion object { /** * Empty instrument data */ val NONE = InstrumentData( "", "", "", "", "", "", ) } } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Orders Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Orders repository This module manages the user's list of trading orders, providing an `OrdersRepository` interface to create, update, remove, and filter orders. It also includes the `Order` data model, which defines the details of each order, including optional take-profit and stop-loss levels for risk management. OrdersRepository Interface: {` interface OrdersRepository { /** * Adds a new order to the repository. * * @param order The [Order] object to be added. */ fun createOrder(order: Order) /** * Removes an existing order from the repository. * * @param order The [Order] object to be removed. */ fun removeOrder(order: Order) /** * Updates an existing order in the repository. * * @param newOrder The updated [Order] to be saved in the repository. */ fun updateOrder(newOrder: Order) /** * Retrieves a list of orders filtered by the specified instrument name. * * @param instrumentName The name of the instrument to filter orders by. * @return A list of [Order] objects associated with the specified instrument name. */ fun getOrdersByInstrumentName(instrumentName: String): List } /*****/ /** * Represents an order in the trading application. Each order includes essential details * like its ID, instrument name, type, quantity, price, position, and optional protection parameters. * * @property id Unique identifier for the order. * @property instrumentName Name of the instrument being traded. * @property orderType Type of the order (e.g., BUY_LIMIT, SELL_STOP). * @property quantity Amount of the instrument to be bought or sold. * @property price Target price for the order execution. * @property color Hexadecimal color code representing the visual color of the order. * @property takeProfitPrice Optional target price for taking profit. * @property stopLossPrice Optional target price for stopping loss. */ data class Order( val id: String, val instrumentName: String, val orderType: OrderType, val quantity: Double, val price: Double, val color: String, val takeProfitPrice: Double?, val stopLossPrice: Double?, ) /** * Enum representing the various types of orders, defining the buy/sell and limit/stop types. */ enum class OrderType { BUY_LIMIT, BUY_STOP, SELL_LIMIT, SELL_STOP, } `} --- ## Chart for Android/Configuration/Repositories/Implementation/Overview.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Repositories Implementation {` data class DxChartsConfig( val repositories: Repositories = Repositories(), val providers: DxChartsDataProviders = DxChartsDataProviders(), val initialInstrumentName: String? = null ) { data class Repositories( val studiesRepository: StudiesRepository = LocalStorageDefaultStudiesRepository(listOf()), val aggregationsRepository: AggregationsRepository = LocalStorageDefaultAggregationsRepository(), val colorsRepository: ColorsRepository = LocalStorageDefaultColorsRepository(), val drawingsLineWidthRepo: DrawingsLineWidthRepo = LocalStorageDefaultDrawingsLineWidthRepo(), val drawingsSnapshotsRepo: DrawingsSnapshotsRepo = LocalStorageDefaultDrawingsSnapshotsRepo(), val drawingsTypesRepo: DrawingsTypesRepo = LocalStorageDefaultDrawingsTypesRepo(), val drawingsFirstClickRepo: DrawingsFirstClickRepo = LocalStorageDefaultFirstClickRepo(), val instrumentRepository: InstrumentRepository = LocalStorageDefaultInstrumentRepository(), val settingsRepository: SettingsRepository = LocalStorageDefaultSettingsRepository(), val timeframePresetsRepository: TimeframePresetsRepository = LocalStorageDefaultTimeframePresetsRepository(), val studiesTemplatesRepository: StudiesTemplatesRepository = LocalStorageDefaultStudiesTemplatesRepository(), val highlighterColorsRepository: ColorsRepository = LocalStorageDefaultHighlighterColorsRepository(), val ordersRepository: OrdersRepository = LocalStorageDefaultOrdersRepository(), ) } `} {` // Repositories creation setContent { DxChartsTheme(/* theme configuration */) { DxChartsScreen( dxChartsConfig = DxChartsConfig( repositories = DxChartsConfig.Repositories(/* implemented repositories */), //... ), //... ) } } //... `} --- ## Chart for Android/Configuration/Repositories/Implementation/Settings Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Settings Repository - Repository for storing information about the installed chart configuration and providing it in aggregated form (for passing it into the chart). - Contains business logic with rules for enabling/disabling interdependent menu items. - Contains a list of time zones. - Items available on the configuration settings screen are configured within the repository. Repository interface: {` /** * Repository for storing chart settings data. * * Default chart settings are set in the implementation of this interface. * * Methods are used when interacting with the settings screen elements. * * Implementation of this class is mandatory for the library to function. It is passed to [com.devexperts.dxcharts.lib.domain.DxChartsConfig]. * * Standard implementation is [com.devexperts.dxcharts.lib.data.repo.default_repos.DefaultSettingsRepository]. */ interface SettingsRepository { /** * Method to set the value of a settings item with Boolean type by passing a value. */ fun toggleItem(settingsItem: SettingsScreenItem, value: Boolean) /** * Method to set the value of a settings item with Boolean type by toggling the value. */ fun toggleItem(settingsItem: SettingsScreenItem) /** * Method to set the value of a settings item with Color type. */ fun changeColor(settingsItem: SettingsScreenItem, value: Color) /** * Method to set the value of a settings item with [HasStringRes] type (dropdown list). */ fun selectDropdownItem(settingsItem: SettingsScreenItem, value: HasStringRes) /** * Method to retrieve the complete chart configuration considering all settings. */ fun getConfiguration(): ChartConfigurationData /** * Method to get the selected time zone. */ fun getSelectedTimezone(): Timezones.TimeZoneItem /** * Method to set the time zone for use in the chart. */ fun setSelectedTimezone(timeZoneItem: Timezones.TimeZoneItem) /** * Method to get the default chart settings set on the first run of the library or when resetting all settings. */ fun getDefaultConfiguration(): ChartConfigurationData /** * Method to get the list of available settings items. */ fun availableSettingsItems(): List /** * Method to get the list of available time zones. */ fun availableTimezones(): List /** * Method to get the list of settings items that should be disabled. */ fun disabledSettingsItems(): Collection /** * Method to notify that the chart aggregation has changed, leading to changes in some settings items. */ fun aggregationChanged(aggregation: Aggregation) /** * Returns whether the trading buttons feature is enabled. */ fun isTradingButtonsFeatureEnabled(): Boolean /** * Enables or disables the trading buttons feature. * * @param isEnabled true to enable the feature, false to disable it. */ fun setTradingButtonsFeatureEnabled(isEnabled: Boolean) /** * Method to set the default chart settings. */ fun resetDefaultConfig() } `} Classes used in the repository: - `com.devexperts.dxcharts.lib.domain.SettingsScreenItem` - an item in the settings list, listing all available settings options inside: {` /** * Class for storing data about a settings item. * * @property submenu The submenu [SettingsSubmenu] where the item is located. * @property stringRes String resource with the name of the item for display in the UI. * @property type The type of settings item [SettingsItemType]. */ enum class SettingsScreenItem( val submenu: SettingsSubmenu, @StringRes val stringRes: Int, val type: SettingsItemType, ) { CHART_TYPE( submenu = SettingsSubmenu.GENERAL, stringRes = R.string.dxcharts_settings_chart_type, type = SettingsItemType.Dropdown, ), HIGH_AND_LOW( submenu = SettingsSubmenu.GENERAL, stringRes = R.string.dxcharts_settings_high_and_low, type = SettingsItemType.Toggle ), VERTICAL_GRID( submenu = SettingsSubmenu.GENERAL, stringRes = R.string.dxcharts_settings_vertical_grid, type = SettingsItemType.Toggle ), HORIZONTAL_GRID( submenu = SettingsSubmenu.GENERAL, stringRes = R.string.dxcharts_settings_horizontal_grid, type = SettingsItemType.Toggle ), CANDLE_WICKS( submenu = SettingsSubmenu.GENERAL, stringRes = R.string.dxcharts_settings_candle_wicks, type = SettingsItemType.Toggle ), WATERMARK( submenu = SettingsSubmenu.GENERAL, stringRes = R.string.dxcharts_settings_watermark, type = SettingsItemType.Toggle ), TIMEZONE( submenu = SettingsSubmenu.GENERAL, stringRes = R.string.dxcharts_timezones_title, type = SettingsItemType.Timezone ), BULLISH_BODY( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_bullish_body, type = SettingsItemType.Color ), BEARISH_BODY( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_bearish_body, type = SettingsItemType.Color ), DOJI( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_doji, type = SettingsItemType.Color ), BULLISH_BORDER( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_bullish_border, type = SettingsItemType.Color ), BEARISH_BORDER( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_bearish_border, type = SettingsItemType.Color ), UP( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_up, type = SettingsItemType.Color ), DOWN( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_down, type = SettingsItemType.Color ), NONE( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_none, type = SettingsItemType.Color ), AREA( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_area, type = SettingsItemType.Color ), SCATTER( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_scatter, type = SettingsItemType.Color ), VOLUME_BULLISH( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_volume_bullish, type = SettingsItemType.Color ), VOLUME_BEARISH( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_volume_bearish, type = SettingsItemType.Color ), VOLUME_DOJI( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_volume_doji, type = SettingsItemType.Color ), BACKGROUND( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_background, type = SettingsItemType.Color ), WATERMARK_COLOR( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_watermark_color, type = SettingsItemType.Color ), GRID( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_grid, type = SettingsItemType.Color ), VALUES_ON_SCALES( submenu = SettingsSubmenu.COLORS, stringRes = R.string.dxcharts_settings_values_on_scales, type = SettingsItemType.Color ), TRADING_FROM_CHART( submenu = SettingsSubmenu.TRADING, stringRes = R.string.dxcharts_settings_trading_from_chart, type = SettingsItemType.Toggle ), SHOW_ACTIVE_ORDERS( submenu = SettingsSubmenu.TRADING, stringRes = R.string.dxcharts_settings_show_active_orders, type = SettingsItemType.Toggle ), SHOW_OPEN_POSITIONS( submenu = SettingsSubmenu.TRADING, stringRes = R.string.dxcharts_settings_show_open_positions, type = SettingsItemType.Toggle ), AUTO_SCALE_PRICE_AXIS( submenu = SettingsSubmenu.SCALES, stringRes = R.string.dxcharts_settings_auto_scale_price_axis, type = SettingsItemType.Toggle ), FIT_STUDIES( submenu = SettingsSubmenu.SCALES, stringRes = R.string.dxcharts_settings_fit_studies, type = SettingsItemType.Toggle ), FIT_ORDERS( submenu = SettingsSubmenu.SCALES, stringRes = R.string.dxcharts_settings_fit_orders, type = SettingsItemType.Toggle ), FIT_POSITIONS( submenu = SettingsSubmenu.SCALES, stringRes = R.string.dxcharts_settings_fit_positions, type = SettingsItemType.Toggle ), INVERT_SCALE( submenu = SettingsSubmenu.SCALES, stringRes = R.string.dxcharts_settings_invert_scale, type = SettingsItemType.Toggle ), LOCK_SCALE( submenu = SettingsSubmenu.SCALES, stringRes = R.string.dxcharts_settings_lock_scale, type = SettingsItemType.Toggle ), SCALE_TYPE( submenu = SettingsSubmenu.SCALES, stringRes = R.string.dxcharts_settings_scale_type, type = SettingsItemType.Dropdown ), SESSION_BREAKS( submenu = SettingsSubmenu.DATA, stringRes = R.string.dxcharts_settings_session_breaks, type = SettingsItemType.Toggle ), EXTENDED_HOURS( submenu = SettingsSubmenu.DATA, stringRes = R.string.dxcharts_settings_extended_hours, type = SettingsItemType.Toggle ), ALIGN_DATA_WITH_SESSION_START( submenu = SettingsSubmenu.DATA, stringRes = R.string.dxcharts_settings_align_data_with_session_start, type = SettingsItemType.Toggle ), PRICE_TYPE( submenu = SettingsSubmenu.DATA, stringRes = R.string.dxcharts_settings_price_type, type = SettingsItemType.Dropdown ), VOLUME( submenu = SettingsSubmenu.DATA, stringRes = R.string.dxcharts_settings_volume, type = SettingsItemType.Toggle ), EVENTS_ON_CHART( submenu = SettingsSubmenu.EVENTS, stringRes = R.string.dxcharts_settings_events_on_chart, type = SettingsItemType.Toggle ), DIVIDENDS( submenu = SettingsSubmenu.EVENTS, stringRes = R.string.dxcharts_settings_dividends, type = SettingsItemType.Toggle ), SPLITS_AND_CONSOLIDATIONS( submenu = SettingsSubmenu.EVENTS, stringRes = R.string.dxcharts_settings_splits_and_consolidations, type = SettingsItemType.Toggle ), EARNINGS_AND_ESTIMATES( submenu = SettingsSubmenu.EVENTS, stringRes = R.string.dxcharts_settings_earnings_and_estimates, type = SettingsItemType.Toggle ), CONFERENCE_CALLS( submenu = SettingsSubmenu.EVENTS, stringRes = R.string.dxcharts_settings_conference_calls, type = SettingsItemType.Toggle ), NEWS( submenu = SettingsSubmenu.EVENTS, stringRes = R.string.dxcharts_settings_news, type = SettingsItemType.Toggle ); /** * Enumeration of available settings submenus. * Used for grouping settings items in the UI. * * @property stringRes String resource with the name of the submenu for display in the UI. */ enum class SettingsSubmenu(@StringRes val stringRes: Int) { GENERAL(R.string.dxcharts_settings_submenu_general), COLORS(R.string.dxcharts_settings_submenu_colors), TRADING(R.string.dxcharts_settings_submenu_trading), SCALES(R.string.dxcharts_settings_submenu_scales), DATA(R.string.dxcharts_settings_submenu_data), EVENTS(R.string.dxcharts_settings_submenu_events), } /** * Enumeration of settings item types. * Used to determine the type of settings item in the UI. * * - [Dropdown] - dropdown list with selectable options * - [Color] - color picker * - [Toggle] - on/off toggle * - [Timezone] - timezone selection */ sealed class SettingsItemType { data object Dropdown : SettingsItemType() data object Color : SettingsItemType() data object Toggle : SettingsItemType() data object Timezone : SettingsItemType() } /** * @property dropdownItems Options for selection in dropdown lists within settings items. */ companion object { val dropdownItems = mapOf>( CHART_TYPE to ChartType.entries, SCALE_TYPE to ScaleType.entries, PRICE_TYPE to PriceType.entries, THEME to Theme.entries, ) } `} - `com.devexperts.dxcharts.lib.domain.HasStringRes `- interface for settings items with a dropdown list displayed on the settings screen: - com.devexperts.dxcharts.lib.domain.ChartType - com.devexperts.dxcharts.lib.domain.PriceType - com.devexperts.dxcharts.lib.domain.ScaleType {` /** * Interface for data displayed on the settings screen in items with a dropdown list. * * Classes implementing this interface: * - [com.devexperts.dxcharts.lib.domain.ChartType] * - [com.devexperts.dxcharts.lib.domain.PriceType] * - [com.devexperts.dxcharts.lib.domain.ScaleType] */ interface HasStringRes { /** * Method to get StringRes with the item name */ @StringRes fun getStringRes(): Int } `} - `com.devexperts.dxcharts.lib.domain.ChartConfigurationData` - data class containing all parameters for chart configuration {` /** * Data class containing all parameters for chart configuration. * * Management parameters is happening from the settings screen. * * The full chart configuration is obtained from [com.devexperts.dxcharts.lib.data.repo.SettingsRepository]. * * @param general General settings * @param colors Chart and data colors * @param trading Parameters related to trading on the chart * @param scales Chart scaling and axis control * @param data Data display on the chart * @param events Event display on the chart * @param unchangeableColors Colors that cannot be changed from the settings screen, set by the developer */ data class ChartConfigurationData( val general: General, val colors: Colors, val trading: Trading, val scales: Scales, val data: Data, val events: Events, val unchangeableColors: UnchangeableColors ) { data class General( val chartType: ChartType, val highAndLow: Boolean, val verticalGrid: Boolean, val horizontalGrid: Boolean, val candleWicks: Boolean, val watermark: Boolean, val timezone: Timezones.TimeZoneItem ) data class Colors( val bullish: Color, val bearish: Color, val doji: Color, val bullishBorder: Color, val bearishBorder: Color, val area: Color, val scatter: Color, val volumeBullish: Color, val volumeBearish: Color, val volumeDoji: Color, val background: Color, val watermark: Color, val grid: Color, val valuesOnScales: Color, val highLowValues: Color, val paneResizer: Color, val crosstool: Color, val drawingsText: Color, ) data class Trading( val tradingFromChart: Boolean, val showActiveOrders: Boolean, val showOpenPositions: Boolean ) data class Scales( val autoScale: Boolean, val fitStudies: Boolean, val fitOrders: Boolean, val fitPositions: Boolean, val invertScale: Boolean, val lockScale: Boolean, val scaleType: ScaleType ) data class Data( val sessionBreaks: Boolean, val extendedHours: Boolean, val alignDataWithSessionStart: Boolean, val priceType: PriceType, val volume: Boolean, ) data class Events( val eventsOnChart: Boolean, val dividends: Boolean, val splitsAndConsolidations: Boolean, val earningsAndEstimates: Boolean, val conferenceCalls: Boolean, val news: Boolean ) data class UnchangeableColors( val highlights: Highlights ) companion object { val EMPTY = ChartConfigurationData( general = General( chartType = ChartType.CANDLE, highAndLow = false, verticalGrid = false, horizontalGrid = false, candleWicks = false, watermark = false, timezone = Timezones.TimeZoneItem( name = "UTC", fullName = "UTC", offset = 0, ) ), colors = Colors( bullish = Color(0xFF000000), bearish = Color(0xFF000000), doji = Color(0xFF000000), bullishBorder = Color(0xFF000000), bearishBorder = Color(0xFF000000), area = Color(0xFF000000), scatter = Color(0xFF000000), volumeBullish = Color(0xFF000000), volumeBearish = Color(0xFF000000), volumeDoji = Color(0xFF000000), background = Color(0xFF000000), watermark = Color(0xFF000000), grid = Color(0xFF000000), valuesOnScales = Color(0xFF000000), highLowValues = Color(0xFF000000), paneResizer = Color(0xFF000000), crosstool = Color(0xFF000000), drawingsText = Color(0xFF000000), ), trading = Trading( tradingFromChart = false, showActiveOrders = false, showOpenPositions = false, ), scales = Scales( autoScale = false, fitStudies = false, fitOrders = false, fitPositions = false, invertScale = false, lockScale = false, scaleType = ScaleType.REGULAR, ), data = Data( sessionBreaks = false, extendedHours = false, alignDataWithSessionStart = false, priceType = PriceType.LAST, volume = false, ), events = Events( eventsOnChart = false, dividends = false, splitsAndConsolidations = false, earningsAndEstimates = false, conferenceCalls = false, news = false, ), unchangeableColors = UnchangeableColors( highlights = Highlights( afterMarket = Highlights.Highlight( background = "", border = "", label = "" ), preMarket = Highlights.Highlight( background = "", border = "", label = "" ), regular = Highlights.Highlight( background = "", border = "", label = "" ), noTrading = Highlights.Highlight( background = "", border = "", label = "" ), ) ) ) } } `} - `com.devexperts.dxcharts.lib.data.model.Timezones.TimeZoneItem` - class used to store information about a timezone. {` object Timezones { /** * Constant representing the UTC timezone used in the timezone list. */ val UTC = TimeZoneItem("UTC", "Etc/UTC") /** * Constant representing the Exchange timezone used in the timezone list. */ val Exchange = TimeZoneItem("Exchange", "Exchange") /** * Data class used to store information about a timezone. * * @param name The display name of the timezone in the timezone list (e.g., Buenos Aires). * @param fullName The full name of the timezone (e.g., America/Argentina/Buenos_Aires). * @param offset The timezone offset relative to UTC in seconds. */ data class TimeZoneItem( val name: String, val fullName: String, val offset: Int? = null ) } `} Link to the default implementation of the repository - https://stash.in.devexperts.com/projects/DXCHARTS/repos/dxcharts-android/browse/dxcharts_lib/src/main/java/com/devexperts/dxcharts/lib/data/repo/default_repos/DefaultSettingsRepository.kt. The documentation of the rules for switching items in the settings list is also located there: - Events on Chart - Trading from Chart - Auto scale price axis - Extended hours - Scale Type List of the names of time zones used in the library - https://stash.in.devexperts.com/projects/DXCHARTS/repos/dxcharts-android/browse/dxcharts_lib/src/main/java/com/devexperts/dxcharts/lib/data/implementation/DefaultSettingsRepository.kt#730. #### Exchanging settings between mobile and web versions If you want to exchange settings from mobile version to web version of charts, [here](https://stash.in.devexperts.com/projects/DXCHARTS/repos/dxcharts-android/browse/dxcharts_lib/src/main/java/com/devexperts/dxcharts/lib/domain/ChartConfigurationDataWeb.kt) you can find the following classes and methods for converting settings between mobile and web versions: - `com.devexperts.dxcharts.lib.domain.ChartConfigurationDataWeb` - data class containing all parameters for chart configuration in the web version - `ChartConfigurationData.toWeb()` - method for converting the mobile version settings to the web version settings - `ChartConfigurationDataWeb.fromWeb()` - method for converting the web version settings to the mobile version settings --- ## Chart for Android/Configuration/Repositories/Implementation/Studies Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Studies Repository Repository for storing information about: - Available indicators for using in the chart. - Enabled indicators (on the chart). - Favorites user's indicators. Repository interface: {` /** * Interface for a repository to store indicators settings data in the library. * * Implementation of this class is mandatory for the library to function. It is passed to [com.devexperts.dxcharts.lib.domain.DxChartsConfig]. * * Standard implementation is [com.devexperts.dxcharts.lib.data.repo.default_repos.DefaultStudiesRepository]. */ interface StudiesRepository { /** * Method to get default settings for an indicator by its id. */ fun getDefaultStudySettingsById(id: String): StudiesSetting? /** * Method to get default settings for all indicators. */ fun getDefaultStudies(): List /** * Method to get settings for all available indicators. */ fun getAvailableStudies(): List /** * Method to get settings for favorite indicators. */ fun getFavoritesStudies(): List /** * Method to get settings for selected (added to the chart) indicators. */ fun getSelectedStudies(): List /** * Method to toggle an indicator's inclusion in the favorites list. */ fun toggleFavoriteStudy(value: StudiesSetting) /** * Method to toggle an indicator's inclusion on the chart, adding it to the selected list. */ fun toggleStudy(value: StudiesSetting) /** * Method to get the settings of an indicator by its id. */ fun findStudyById(id: String): StudiesSetting? /** * Method to update the settings of an indicator. */ fun updateStudy(study: StudiesSetting) /** * Method to clear the list of selected indicators, disabling indicators displayed on the chart. */ fun deselectAll() } `} The class `com.devexperts.dxcharts.provider.domain.StudiesSetting` used in the repository: {` /** * Data class representing the settings for a study in a financial indicator. * * @property id The unique identifier for the study setting. * @property title The title of the study. * @property uuid The universally unique identifier (UUID) for the study. * @property type The type of the indicator associated with the study. * @property parameters The list of parameters for the study. * @property lines The list of lines for the study. * @property overlaying A boolean indicating whether the study is overlaying on the chart. * @property calculateFutureData A boolean indicating whether to calculate future data for the study. * @property categories A string representing the categories associated with the study. * @property locked A nullable boolean indicating whether the study is locked. */ data class StudiesSetting( val id: String, val title: String, val uuid: String, val type: IndicatorType, val parameters: List, val lines: List, val overlaying: Boolean, val calculateFutureData: Boolean, val categories: String, val locked: Boolean? ) { /** * Data class representing a line associated with a study. * * @property title The title of the line. * @property type The type of the study line. * @property thickness The thickness of the line. * @property colors The colors associated with the line, which should be in either rgba format (e.g., "rgba(255, 0, 0, 1)") or hex format (e.g., "#FF0000FF"). * @property visible A boolean indicating whether the line is visible. */ data class Line( val title: String?, val type: Type?, val thickness: Int?, val colors: List?, val visible: Boolean? ) { /** * Enum class representing the types of study lines. * * Values: [POINTS], [LINEAR], [HISTOGRAM], [DIFFERENCE], [ABOVE_CANDLE_TEXT], * [TEXT], [BELOW_CANDLE_TEXT], [ABOVE_CANDLE_TRIANGLE], [TRIANGLE], * [COLOR_CANDLE], [RECTANGULAR]; */ enum class Type { POINTS, LINEAR, HISTOGRAM, DIFFERENCE, ABOVE_CANDLE_TEXT, TEXT, BELOW_CANDLE_TEXT, ABOVE_CANDLE_TRIANGLE, TRIANGLE, COLOR_CANDLE, RECTANGULAR; } } /** * Data class representing a parameter associated with a study. * * @property id The unique identifier for the parameter. * @property type The type of the study parameter. * @property value The value of the parameter. * @property validation The [Validation] rules for the parameter. * @property visible A boolean indicating whether the parameter is visible. */ data class Parameter( val id: String, val type: Type, val value: Any?, val validation: Validation?, val visible: Boolean? ) { /** * Data class representing validation rules for a study parameter. * * @property min The minimum value allowed for the parameter. * @property max The maximum value allowed for the parameter. * @property precision The precision of the parameter value. */ data class Validation( val min: Double?, val max: Double?, val precision: Int?, ) /** * Enum class representing the types of study parameters. * * Values: [INTEGER_RANGE], [DOUBLE_RANGE], [PRICE_FIELD], [STRING], [AGGREGATION], [BOOLEAN], [AVERAGE], [UNDEFINED] */ enum class Type { INTEGER_RANGE, DOUBLE_RANGE, PRICE_FIELD, STRING, AGGREGATION, BOOLEAN, AVERAGE, UNDEFINED, } } } `} In the default implementation, it is necessary to pass a list of available indicators. The loading of indicators for the default implementation is done using `com.devexperts.dxcharts.lib.data.implementation.StudiesLoader` . --- ## Chart for Android/Configuration/Repositories/Implementation/Studies Templates Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Studies Templates Repository Repository stores user's list of indicators templates. Repository interface: {` /** * Interface for a repository to store data about user-created templates of indicators. * * Implementation of this interface is mandatory for the library to function. It is passed to [com.devexperts.dxcharts.lib.domain.DxChartsConfig]. * * Standard implementation: [com.devexperts.dxcharts.lib.data.repo.default_repos.DefaultStudiesTemplatesRepository] */ interface StudiesTemplatesRepository { /** * Method to add or remove a template to the list of enabled templates. */ fun toggleTemplate(template: TemplatesModel) /** * Method to save a new template. */ fun saveTemplate(template: TemplatesModel) /** * Method to remove a template by [templateId]. */ fun removeTemplate(templateId: String) /** * Method to update an existing template. */ fun updateTemplate(template: TemplatesModel) /** * Method to check if a template name is occupied. * * @return true - if the name is occupied, false - if it's free */ fun isTemplateNameOccupied(templateName: String): Boolean /** * Method to get all templates. */ fun getTemplates(): List /** * Method to get templates that have been added to the list of enabled templates. */ fun getSelectedTemplate(): TemplatesModel? /** * Method to get a template by its [id]. * * @return the template if it exists, null - if it doesn't exist */ fun findTemplateById(id: String): TemplatesModel? /** * Method to remove a template from the list of enabled templates. */ fun deselectTemplate() } `} `TemplatesModel` class used in the repository: {` data class TemplatesModel ( val id: String = "", var name: String = "", val studies: MutableList = mutableListOf() ) `} --- ## Chart for Android/Configuration/Repositories/Implementation/Timeframe Presets Repository.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Timeframe Presets Repository Repository for storing Timeframe Presets list. Repository interface: {` /** * Interface for a repository to store data about the selected timeframe on the chart. * * Implementation of this interface is mandatory for the library to function. It is passed to [com.devexperts.dxcharts.lib.domain.DxChartsConfig]. * * Standard implementation: [com.devexperts.dxcharts.lib.data.repo.default_repos.DefaultTimeframePresetsRepository] */ interface TimeframePresetsRepository { /** * Method to retrieve a list of user-created and standard timeframes. */ fun getItems(): List /** * Method to set a timeframe as selected. */ fun select(value: TimeframePreset) /** * Method to get the selected timeframe. */ fun getSelected(): TimeframePreset? } `} TimeframePreset class used in the repository: {` /** * Data-class for storing data about timeframes presets * * @property name Name of the preset * @property nameRes String resource with name of the preset, could be null * @property range Range of the preset in milliseconds */ data class TimeframePreset( val name: String, @StringRes val nameRes: Int? = null, val range: Long, val aggregation: Aggregation ) { /** * Storing constants for default timeframes presets */ object Range { const val HOUR = 60L * 60L * 1000L const val DAY = 24L * HOUR const val WEEK = 7L * DAY const val MONTH = 30L * DAY const val MONTH_3 = 90L * DAY const val MONTH_6 = 180L * DAY const val YEAR = 365 * DAY } } `} --- ## Chart for Android/Configuration/Repositories/Overview.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Repositories Repositories stores data during the library's operation and for setting the business logic of certain modules. Interfaces for implementing repositories are located in the directory `com.devexperts.dxcharts.lib.data.repo`. If you need to change logic of our default implementations of repositories, e.g. change default values, you need to create your own implementation of according repositories and pass them into `DxChartsConfig.Repositories` | Repository name | Interface path | Default implementation path | Description | |-------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Aggregations Repository | `com.devexperts.dxcharts.lib.data.repo.AgregationsRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultAggregationsRepository` | Repository for storing information about created and selected aggregations | | Colors Repository | `com.devexperts.dxcharts.lib.data.repo.ColorsRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultColorsRepository` | Repository for storing color palette for indicators, settings, and drawings (except for the Highlighter type of drawings) | | Highlighter Colors Repository | `com.devexperts.dxcharts.lib.data.repo.ColorsRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultHighlighterColorsRepository` | Repository for storing color palette for the Highlighter drawing | | Drawings Repository | `com.devexperts.dxcharts.lib.data.repo.DrawingsRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultDrawingsRepository` | Repository for storing drawings system data:

Contains 4 child interfaces that handle data about:
1) IInitial press of "Magnet" and "Brush" buttons on the drawing toolbar - a hint should be shown on the first press, and it also stores whether the hint was shown after setting a text drawing.
2) Selected line thickness for drawings.
3) Selected drawings from the general list displayed in the corresponding toolbar.
4) Snapshots of drawings created on the chart. | | Instrument Repository | `com.devexperts.dxcharts.lib.data.repo.InstrumentRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultInstrumentRepository` | Repository to store data about the selected instrument on the chart | | Settings Repository | `com.devexperts.dxcharts.lib.data.repo.SettingsRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultSettingsRepository` | Repository for storing chart settings data | | Studies Repository | `com.devexperts.dxcharts.lib.data.repo.StudiesRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultStudiesRepository` | Repository to store indicators settings data | | Studies Templates Repository | `com.devexperts.dxcharts.lib.data.repo.StudiesTemplatesRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultStudiesTemplatesRepository` | Repository to store data about user-created templates of indicators. | | Timeframe Presets Repository | `com.devexperts.dxcharts.lib.data.repo.TimeframeTemplatesRepository` | `com.devexperts.dxcharts.lib.data.repo.default_repos.LocalStorageDefaultTimeframePresetsRepository` | Repository to store data about timeframe presets. | By default, data is not stored on the device - all data is cleared upon application reload. To prevent this from happening, the library user needs to implement the necessary repositories or you can use library implementation of repositories from `serializable_storage_repos` package. They have methods `serialize()` and `deserialize(data: String)` to save and load data from your app. `serialize()` method is called everytime when repository have some changes in its state and it save its state into shared preferences. According to that you also need to pass `context` as a parameter into serializable storage. All you need is to create objects of the necessary repositories and pass them to the library using the `DxChartsConfig` and use `deserialize(data: String)` method: {` private val studiesRepository by lazy { SerializableStorageDefaultStudiesRepository(application, StudiesLoader().load(assets)) } private val aggregationsRepository by lazy { SerializableStorageDefaultAggregationsRepository(application) } private val colorsRepository by lazy { SerializableStorageDefaultColorsRepository(application) } private val drawingsLineWidthRepo by lazy { SerializableStorageDefaultDrawingsLineWidthRepo(application) } private val drawingsSnapshotsRepo by lazy { SerializableStorageDefaultDrawingsSnapshotsRepo(application) } private val drawingsFirstClickRepo by lazy { SerializableStorageDefaultFirstClickRepo(application) } private val drawingsTypesRepo by lazy { SerializableStorageDefaultDrawingsTypesRepo(application) } private val highlighterColorsRepository by lazy { SerializableStorageDefaultHighlighterColorsRepository(application) } private val instrumentRepository by lazy { SerializableStorageDefaultInstrumentRepository(application) } private val timeframePresetsRepository by lazy { SerializableStorageDefaultTimeframePresetsRepository(application) } private val settingsRepository by lazy { SerializableStorageDefaultSettingsRepository(application) } private val studiesTemplatesRepository by lazy { SerializableStorageDefaultStudiesTemplatesRepository(application) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) deserializeRepositories() //... setContent { DxChartsTheme { Surface( modifier = Modifier.fillMaxSize(), color = DxChartsTheme.colors.mainChartBg ) { DxChartsScreen( DxChartsConfig( repositories = DxChartsConfig.Repositories( studiesRepository = studiesRepository, aggregationsRepository = aggregationsRepository, colorsRepository = colorsRepository, drawingsLineWidthRepo = drawingsLineWidthRepo, drawingsSnapshotsRepo = drawingsSnapshotsRepo, drawingsFirstClickRepo = drawingsFirstClickRepo, drawingsTypesRepo = drawingsTypesRepo, highlighterColorsRepository = highlighterColorsRepository, instrumentRepository = instrumentRepository, timeframePresetsRepository = timeframePresetsRepository, settingsRepository = settingsRepository, studiesTemplatesRepository = studiesTemplatesRepository ), //... } } } } private fun deserializeRepositories() { val prefs = application.getSharedPreferences("dxcharts_demo_app", MODE_PRIVATE) val aggregations = prefs.getString("aggregations_repo", null) val colors = prefs.getString("colors_repo", null) val drawingsLine = prefs.getString("drawings_line_repo", null) val drawingsSnapshots = prefs.getString("drawings_snapshots_repo", null) val drawingsTypes = prefs.getString("drawings_types_repo", null) val drawingsFirstClick = prefs.getString("drawings_first_click_repo", null) val highlighter = prefs.getString("highlighter_repo", null) val instrument = prefs.getString("instrument_repo", null) val settings = prefs.getString("settings_repo", null) val studies = prefs.getString("studies_repo", null) val studiesTemplates = prefs.getString("studies_templates_repo", null) val timeframePresets = prefs.getString("timeframe_presets_repo", null) aggregations?.let { aggregationsRepository.deserialize(it) } colors?.let { colorsRepository.deserialize(it) } drawingsLine?.let { drawingsLineWidthRepo.deserialize(it) } drawingsSnapshots?.let { drawingsSnapshotsRepo.deserialize(it) } drawingsTypes?.let { drawingsTypesRepo.deserialize(it) } drawingsFirstClick?.let { drawingsFirstClickRepo.deserialize(it) } highlighter?.let { highlighterColorsRepository.deserialize(it) } instrument?.let { instrumentRepository.deserialize(it) } settings?.let { settingsRepository.deserialize(it) } studies?.let { studiesRepository?.deserialize(it) } studiesTemplates?.let { studiesTemplatesRepository.deserialize(it) } timeframePresets?.let { timeframePresetsRepository.deserialize(it) } } `} --- ## Chart for Android/Configuration/Strings.md # Strings String resources of the library are stored in the directory *res/values/strings.xml*. Replacement and localization of string resources follow the standard Android development algorithm: 1. To replace string resources without localization, you need to override the modifiable parameters in the *res/values/strings.xml* file inside the Host App. Example: 1. Select the string resource that needs to be modified. In our example, it's the text 'Create custom' on the aggregation selection screen. In the original *strings.xml* file, it looks like this: ``` //... Create custom //... ``` 2. Inside the Host App, in the file *res/values/strings.xml*, create a parameter with the same name as the one being modified. ``` your app name ``` 3. Write the new value for the string parameter ``` your app name Test ``` 4. Run the Host App and ensure that the string resource has been modified ![](../../images/bottomToolbar.png) 2. Localization of string resources in Android is described in the official documentation by Google, and you can follow the guidelines: https://developer.android.com/guide/topics/resources/localization. This allows you to define sets of string resources for multiple languages, and system will automatically select the appropriate set based on the device's locale settings. The original *strings.xml* file with all the parameters used in the library can be found here: https://stash.in.devexperts.com/projects/DXCHARTS/repos/dxcharts-android/browse/dxcharts_lib/src/main/res/values/strings.xml --- ## Chart for Android/Configuration/Theme configuration/Components/Colors palette.md # Colors palette Colors palette for every color used in the library, excluding the chart itself. Colors on the chart are set through the `ColorsRepository`. Each color in the palette is stored as a Color object. Colors are represented in the ARGB (Alpha, Red, Green, Blue) format, where Alpha represents transparency, and Red, Green, Blue represent the corresponding color components. Within the library, the palette is passed in the `DxChartsColors` object, which needs to be passed as a parameter to `DxChartsTheme`. Default colors can be obtained by calling the `darkPalette()` method. How to change color palette you can find [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Using-a-theme/Colors-palette) Colors table: | Value name | HEX (Dark) | Alpha (%) (Dark) | HEX (Light) | Alpha (%) (Light) | |------------------------------|------------|------------------|-------------|-------------------| | mainChartBg | #FF000000 | 100% | #FFFFFFFF | 100% | | mainChartValueText | #FF808080 | 100% | #FF4D4D4D | 100% | | mainChartValueTextBorder | #66000000 | 40% | #66FFFFFF | 40% | | mainChartGridLine | #FF282828 | 100% | #FFE5E5E5 | 100% | | buttonPrimaryDefault | #FF141414 | 100% | #FFF5F5F5 | 100% | | mainChartCandleBullBodyBg | #FF4d9953 | 100% | #FF67AD6D | 100% | | mainChartCandleBearBodyBg | #FFD92C40 | 100% | #FFEB4C4C | 100% | | mainChartIndicatorNameBg | #FFFFFFFF | 100% | #FF404040 | 100% | | mainChartCrosshairLineBg | #FFFFAA00 | 100% | #FFFF5B24 | 100% | | mainChartSymbolName | #FFFFFFFF | 100% | #FF404040 | 100% | | iconPrimaryDefaultBg | #FFD4D4D3 | 100% | #FF1F1F1F | 100% | | inputDefaultText | #FFFFFFFF | 100% | #FF000000 | 100% | | inputTextActive | #FFFFAA00 | 100% | #FFFFFFFF | 100% | | dropdownListItemSelectedText | #FFFFAA00 | 100% | #FFFF5B24 | 100% | | dropdownListItemDefaultText | #FFDFDEDF | 100% | #FF4D4D4D | 100% | | dropdownDescriptionText | #FF70706F | 100% | #FFD1D1D1 | 100% | | databoxTextDefault | #FFE5E4E4 | 100% | #FF4C4242 | 100% | | dropdownDefaultBg | #FF141414 | 100% | #FFFFFFFF | 100% | | iconActiveBg | #FFFFAA00 | 100% | #FFFF5B24 | 100% | | iconActiveDisabledDefaultBg | #FF4D3300 | 100% | #66FF5B24 | 40% | | iconPressedBg | #FFFFAA00 | 100% | #FFFFFFFF | 100% | | iconSecondaryDefaultBg | #FF6E6C6B | 100% | #FFD1D1D1 | 100% | | iconDisabledDefaultBg | #FF6E6C6B | 100% | #FFD1D1D1 | 100% | | buttonPrimaryPressed | #FF433211 | 100% | #FFFF5B24 | 100% | | separator | #FF282828 | 100% | #FFE5E5E5 | 100% | | listBackground | #80000000 | 50% | #80FFFFFF | 50% | | searchRowBg | #FF202020 | 100% | #FF202020 | 100% | | searchRowTextBg | #1CFFFFFF | 11% | #1CFFFFFF | 11% | | colorButtonBorderStrokeColor | #1AFFFFFF | 10% | #1AFFFFFF | 10% | | colorPickerPressed | #FFD4D4D3 | 100% | #FFD4D4D3 | 100% | | bidButtonBg | #3345BB4F | 20% | #FF6FB974 | 100% | | bidButtonTextColor | #FF42b34b | 100% | #FFFFFFFF | 100% | | askButtonBg | #3DD23A2B | 24% | #FFF66857 | 100% | | askButtonTextColor | #FFD23A2B | 100% | #FFFFFFFF | 100% | | buttonDangerDefaultBg | #FFD92C40 | 100% | #FFD92C40 | 100% | | buttonPrimaryDefaultText | #FF292929 | 100% | #FFFFFFFF | 100% | | inputBorderDefaultBg | #FF282828 | 100% | #FFE0E0E0 | 100% | | eventsEarningsBg | #FFD92C40 | 100% | #FFD92C40 | 100% | | eventsDividendsBg | #FFC05BFF | 100% | #FFC05BFF | 100% | | eventsConferenceCallsBg | #FF65DBB2 | 100% | #FF65DBB2 | 100% | | eventsSplitsBg | #FFF4BB3F | 100% | #FFF4BB3F | 100% | | notificationHeaderText | #FFFFAA00 | 100% | #FFFF5B24 | 100% | | notificationBodyText | #FF666666 | 100% | #FF4D4D4D | 100% | | searchRowText | #FFE5E5E5 | 100% | #FFE5E5E5 | 100% | | searchRowContinuationText | #FF6E6C6B | 100% | #FF6E6C6B | 100% | | buttonBorderBg | #FF282828 | 100% | #FFE5E5E5 | 100% | | tabDefaultBg | #FF141414 | 100% | #FFEBEBED | 100% | | tabActiveBg | #FFFFAA00 | 100% | #FFFFFFFF | 100% | | tabTextDefault | #FFDFDEDF | 100% | #FF292929 | 100% | | tabTextActive | #FF292929 | 100% | #FF292929 | 100% | | suggestHighlightBg | #33FFAA00 | 20% | #FFFF5B24 | 100% | | suggestHighlightText | #FFFFAA00 | 100% | #FFFFFFFF | 100% | | buyOrderBg | #FF0E2510 | 100% | #800E2510 | 50% | | sellOrderBg | #FF320E0A | 100% | #80320E0A | 50% | | protectionText | #FFFFAA00 | 100% | #FFFFAA00 | 100% | | protectionBg | #FF332200 | 100% | #80332200 | 50% | | protectionSetupText | #FFFFFFFF | 100% | #FF404040 | 100% | | plusOrderButtonBg | #FF332200 | 100% | #FFFF5B24 | 100% | | plusOrderButton | #FFFFAA00 | 100% | #FFFFFFFF | 100% | DxChartsColors file and default colors values - https://stash.in.devexperts.com/projects/DXCHARTS/repos/dxcharts-android/browse/dxcharts_lib/src/main/java/com/devexperts/dxcharts/lib/ui/theme/Colors.kt. --- ## Chart for Android/Configuration/Theme configuration/Components/Dimensions.md # Dimensions Class `com.devexperts.dxcharts.lib.ui.theme.DxChartDimensions` contains a list of sizes for elements, paddings, and margins - all values in Dp, used in the library. The class is passed as a parameter in DxChartsTheme. How to override default dimensions you can find [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Using-a-theme/Dimensions) - Elements are divided into 19 subclasses: - Aggregations - aggregation screen - ActionBar - top action bar - BottomToolbar - bottom toolbar - CandleInfoWidget - top widget with candle and OHLC information - CenterText - informational screen about the absence of elements in the list - ColorPicker - color selection widget - Common - common parameters - DrawingsList - drawings list - Events - events displayed on the chart - ListItem - list item - SettingsScreen - settings screen - Indicators - indicator system - InstrumentsList - instruments list - TimezonesScreen - timezone selection screen - NewsPopup - news displayed on the chart - HintCard - Hint widget - TradingWidget - trading widget - RangeSelectorWidget - timeframe preset selection widget - SearchRow - search row - Orders - trading orders Elements have four types of values: - `androidx.compose.ui.unit.Dp` - virtual pixel unit that's roughly equal to one pixel on a medium-density screen - `androidx.compose.foundation.layout.PaddingValues` - padding values for the element with the following parameter sets: top, bottom, start, end, or vertical and horizontal. Parameters have type Dp. - `androidx.compose.ui.unit.DpSize` - element size with the following parameters: width, height. Parameters have type Dp. - `@Composable (Unit) -> dp` methods - method for calculating Сomposable values from Сompose methods. Class Dimensions with a list of default values - https://stash.in.devexperts.com/projects/DXCHARTS/repos/dxcharts-android/browse/dxcharts_lib/src/main/java/com/devexperts/dxcharts/lib/ui/theme/Dimensions.kt --- ## Chart for Android/Configuration/Theme configuration/Components/Icons.md # Icons All icons in the library are divided into 4 groups. Each group is passed to the library through their interfaces by providing them as parameters in DxChartsTheme. To define an icon in the application, `the class androidx.compose.ui.graphics.vector.ImageVector` is used. Each icon's getter has the `@Composable` annotation. ```kotlin interface CommonIcons { /... val Ok: ImageVector @Composable get /... } ``` How to replace default icons with custom ones you can find [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Using-a-theme/Icons) Tables of icons used in the library: - Common Icons: These icons are used throughout the library for various common actions such as erasing, adding, deleting, etc. | Value name | Default value | |------------|-----------------------------------------------------------------------| | Erase | erase | | Bin | bin | | Tick | tick | | Back | back | | Close | cross | | Search | search | | RightArrow | forward | | RemoveText | close | | Ok | tick | | Add | add | | Delete | remove | | Plus | plus | - Studies Icons: These icons are used in the studies section of the library. | Value name | Default value | |--------------------|-----------------------------------------------------------------------------| | LineType | ind_1 | | ListItem | ind_2 | | ListItemSelected | circle | | FavStudies | favorite | | FavStudiesSelected | unfavorite | - Toolbar Icons: These icons are used in the toolbar of the library. | Value name | Default value | |-------------|-----------------------------------------------------------------------------------| | Search | search | | Drawings | drawings_icon | | Studies | ind_icon | | Trade | dollar | | Settings | settings | | Magnet | magnet | | Brush | brush | | LineWidth | line_width | | ColorPicker | circle | | Plus | large_plus | | TextSize | text_size | - Top Bar Icons: These icons are used in the top bar which is shown when drawing mode is active of the library. | Value name | Default Value | |--------------|---------------------------------------------------------------------------------------| | ArrowBack | drawing_back | | ArrowForward | drawing_forward | --- ## Chart for Android/Configuration/Theme configuration/Components/Shapes.md # Shapes Class `com.devexperts.dxcharts.lib.ui.theme.Shape` contains parameters for rounding UI elements in the form of `androidx.compose.foundation.shape.RoundedCornerShape`. The class is passed as a parameter in `DxChartsTheme`. How to override shapes used in library can be found [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Using-a-theme/Shapes) Table of elements in the `com.devexperts.dxcharts.lib.ui.theme.Shapes` class: | topStart parameter | topEnd parameter | bottomStart parameter | bottomEnd parameter | Illustration | Values | |--------------------|------------------|-----------------------|---------------------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 4.dp | 4.dp | 4.dp | 4.dp | | lastCandleInfoWidgetShape
deleteOrderShape
orderShape | | 8.dp | 8.dp | 8.dp | 8.dp | | colorPaletteShape
conferenceCallsCardWidgetShape
dividendsCardWidgetShape
earningsCardWidgetShape
splitsCardWidgetShape
newsCardWidgetShape | | 12.dp | 12.dp | 12.dp | 12.dp | | searchRowTextFieldShape
instrumentListCheckedItemBackgroundShape
studiesSettingsTextButtonShape
studiesTextButtonShape
studiesSettingsSelectorButtonShape
studiesSettingsSelectorBackgroundShape
tradingWidgetButtonShape
studiesSettingsNumberButtonShape
createNewTemplateButtonShape
tradingSuspendedTileShape
studiesSettingsDropDownMenuShape
dropdownMenuShape
priceLabelShape
protectionSetupShape
numKeyboardButtonsShape | | 36.dp | 36.dp | 36.dp | 36.dp | | roundButtonStudiesColorShapes | | Circle | Circle | Circle | Circle | | roundButtonStudiesColorShape
textToolbarToggleButtonShape
iconToolbarToggleButtonShape
colorPickerButtonShape
rangeSelectorShape
timeframePresetsSelectorBackgroundShape
colorCircleButtonShape
floatingButtonShape
alphaPickerThumbShape
colorPickerBtnShape
colorAlphaPickerCanvasShape
hintCardShape
floatingButtonsShape
colorCircleShape
rangeSelectorItemWidgetShape
iconToolbarButtonShape
ordersPlusButtonShape | | 22.dp | 22.dp | - | - | | bottomSheetShape | | 8.dp | 8.dp | - | - | | drawingsListFirstItemShape | | - | - | 8.dp | 8.dp | | drawingsListLastItemShape | | 0.dp | 0.dp | 0.dp | 0.dp | | drawingsListItemShape
protectionSetupButtonsShape
customAggregationScreenHolder | | 16.dp | 0.dp | 16.dp | 0.dp | | limitOrderButtonShape | | 0.dp | 16.dp | 0.dp | 16.dp | | stopOrderButtonShape | --- ## Chart for Android/Configuration/Theme configuration/Components/Text styles.md # Text Styles The list of text styles used by the library is passed to `DxChartsTheme` within the `DxChartsTypography` object. It also contains default values for each style. The object used to store information about text style is `androidx.compose.ui.text.TextStyle`. The font within the chart itself is currently immutable. By default, library uses Open Sans SemiBold (https://fonts.google.com/specimen/Open+Sans). Text styles do not include the color parameter; it is set individually for each element based on the color palette parameters. The table of text styles in the library: | Font size (sp) | Values | |----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 130 | aggregationSelectorValue | | 90 | selectorValue | | 36 | aggregationSelectorTimeUnit
numKeyboardButtons | | 32 | templatesHeading | | 20 | listItem | | 16 | noConnectionScreenHeader
noConnectionScreenBody
createTemplateButtonBody
studiesSettingsDropDownMenuItemBody
studiesSettingsNumberButtonBody
studiesTemplatesListAllIndicatorsTitle
templatesListItem
studiesSettingsPlotsItems
studiesSettingsInputsTitle
studiesSettingsInputsBody
studiesSettingsSelectorPlotsButton
studiesSettingsSelectorInputsButton
studiesSettingsPlotsTitle
studiesSettingsStudyTitle
studiesListItem
studiesListTemplatesTitle
studiesListIndicatorsTitle
studiesListFavouritesTitle
studiesListAllIndicatorsTitle
studiesTextButton
studiesSettingsTextButton
dropdownMenuItem
settingsToggleRow
timezoneRowItem
timezoneRowTitle
timezoneListItemValue
timezoneListItemTitle
settingsOnOffSwitch
settingsResetButton
settingsDropdownItem
settingsDropdownRowTitle
settingsDropdownRowValue
settingsColorChangeRowTitle
timezonesListTitle
emptyTimezonesListBody
settingsListSublistTitle
emptySettingsListBody
settingsListTitle
instrumentListItemValue
emptySearchScreenBody
studiesCenterTextButtonBody
drawingsTextInputHintValue
drawingsTextInputBody
drawingsTextInputActionBarDoneButtonBody
drawingsTextInputActionBarCancelButtonBody
drawingsTextInputActionBarTitle
drawingsListItemTitle
drawingsListSublistTitle
drawingsListTitle
colorButtonDeleteButtonValue
customAggregationSelectorAddButton
mainScreenActionBarInstrumentSymbolValue
arrowsActionBarDoneButtonValue
arrowsActionBarCancelButtonValue
searchRowTextFieldBody
searchRowCancelButtonValue
floatingButtonsText
settingsVersionRow
studiesSettingsInputsItem
createOrderButtonsText | | 14 | centerTextScreen | | 14 | hintCardTitle | | 12 | volumeIndicator
tradingWidget
textToolbarToggleButton
timeframeSelectorButton
instrumentListItemDescription
newsWidgetBody
splitsWidgetTitle
earningsWidgetTittle
earningsWidgetBasicEpsTitle
earningsWidgetDilutesEpsTitle
earningsWidgetPeriodEndingTitle
earningsWidgetBasicEpsBody
earningsWidgetDilutesEpsBody
earningsWidgetPeriodEndingBody
dividendsWidgetTittle
dividendsWidgetGrossTitle
dividendsWidgetGrossBody
callsWidgetTitle
callsWidgetDateTitle
callsWidgetDateBody
priceChangeWidgetPercentage
priceChangeWidgetInstrument
candleInfoBody
tradingSuspendedBody
centerTextScreenHeader
centerTextScreenBody
orderEntryText
protectionText | How to override default text styles can be found [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Using-a-theme/Text-Styles ) --- ## Chart for Android/Configuration/Theme configuration/Overview.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Theme Configuration Library theme configuration is done by passing parameters into the Compose function `DxChartsTheme`. The function takes the following parameters: | Name | Parameter name | Parameter Class | Default implementation | Description | |---------------|:---------------|---------------------------------------------------------|----------------------------------------------------------------|---------------------------------------------| | Typography | typography | com.devexperts.dxcharts.lib.ui.theme.DxChartsTypography | com.devexperts.dxcharts.lib.ui.theme.DxChartsTypography | Fonts' types, styles, sizes | | Top Bar Icons | topBarIcons | com.devexperts.dxcharts.lib.ui.theme.icons.TopBarIcons | com.devexperts.dxcharts.lib.ui.theme.icons.DefaultTopBarIcons | Icons used in top bars | | Toolbar Icons | toolbarIcons | com.devexperts.dxcharts.lib.ui.theme.icons.ToolbarIcons | com.devexperts.dxcharts.lib.ui.theme.icons.DefaultToolbarIcons | Icons used in toolbars | | Studies Icons | studiesIcons | com.devexperts.dxcharts.lib.ui.theme.icons.StudiesIcons | com.devexperts.dxcharts.lib.ui.theme.icons.DefaultStudiesIcons | Icons used on screens related to indicators | | Shapes | shapes | com.devexperts.dxcharts.lib.ui.theme.DxChartsShapes | com.devexperts.dxcharts.lib.ui.theme.DxChartsShapes | Parameters for the rounding of UI elements | | Dimensions | dimensions | com.devexperts.dxcharts.lib.ui.theme.DxChartDimensions | com.devexperts.dxcharts.lib.ui.theme.DxChartDimensions | Paddings, margins, sizes | | Common Icons | commonIcons | com.devexperts.dxcharts.lib.ui.theme.icons.CommonIcons | com.devexperts.dxcharts.lib.ui.theme.icons.DefaultCommonIcons | Common library's icons | And the last parameter of the function is `content: @Composable () -> Unit` - Compose function to which the theme will be applied. When you use DxCharts library, this should be the function `com.devexperts.dxcharts.lib.ui.DxChartsScreen`. {` //... DxChartsTheme(/* overriden parameters of default */) { DxChartsScreen( /* overriden parameters of default */ ) } //... `} Within the DxCharts library, parameters are passed using `CompositionLocalProvider`, so the construction described above is essential for the library to function. --- ## Chart for Android/Configuration/Theme configuration/Using a theme/Colors palette.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Colors palette You can change the color palette in two ways: - The complete implementation of the interface `com.devexperts.dxcharts.lib.ui.theme.DxChartsColors`. {` val colors = DxChartsColors( mainChartBg = Color(/* ... */), mainChartValueText = Color(/* ... */), /* Every parameter initialization */ ) DxChartsTheme( colors = colors ) { /* content */ } `} - Adjusting the parameters of the default color palette. It's not mandatory to modify all parameters. {` val colors = darkPalette().copy( iconActiveBg = Color(/* ... */), separator = Color(/* ... */), //... ) DxChartsTheme( colors = colors ) { /* content */ } `} All colors used in library you can find [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Components/Colors-palette) Creating a color is done using the `androidx.compose.ui.graphics.Color` class: - Using constants: `Color.White`, `Color.Black`, `Color.Cyan` - Creating RGB, SRGB, XYZ, LAB colors using the Color class constructor: `Color("0xAABBCCDD")` or `Color(alpha = 0xAA, red = 0xBB, green = 0xCC, blue = 0xDD)` or using another method. --- ## Chart for Android/Configuration/Theme configuration/Using a theme/Dimensions.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Dimensions Changing Dimensions within the application is done by modifying the parameters of the `com.devexperts.dxcharts.lib.ui.theme.DxChartDimensions` class. Parameters that are not modified by user remain at their default values: {` val dimensions = DxChartDimensions( common = Common( bottomToolbarIconSize = 19.dp, //... ), /* Other parameters overriding */ ) DxChartsTheme( dimensions = dimensions ) { /* content */ } `} All dimensions used in the library can be found [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Components/Dimensions) --- ## Chart for Android/Configuration/Theme configuration/Using a theme/Icons.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Icons Icons to replace default ones in the library should be in vector format packed in XML. New icons should have the correct naming format and be stored in the *res/drawable* directory. Access to icons is done by referring to resources and creating a `VectorResource` object (which can only be initialized in a Composable function). Changing DxCharts library icons is done by implementing interfaces in the `com.devexperts.dxcharts.lib.ui.theme.icons` package: 1. `com.devexperts.dxcharts.lib.ui.theme.icons.CommonIcons` 2. `com.devexperts.dxcharts.lib.ui.theme.icons.StudiesIcons` 3. `com.devexperts.dxcharts.lib.ui.theme.icons.ToolbarIcons` 4. `com.devexperts.dxcharts.lib.ui.theme.icons.TopBarIcons` You can find all icons and their names and default implementation [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Components/Icons) {` val commonIcons = object : CommonIcons { override val Erase: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon1) override val Bin: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon2) /* Other values overriding */ } val studiesIcons = object : StudiesIcons { override val ListItem: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon3) override val LineType: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon4) /* Other values overriding */ } val toolbarIcons = object : ToolbarIcons { override val Brush: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon5) override val ChartTypes: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon6) /* Other values overriding */ } val topBarIcons = object : TopBarIcons { override val ArrowBack: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon7) override val ArrowForward: ImageVector @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon8) } DxChartsTheme( commonIcons = commonIcons, studiesIcons = studiesIcons, toolbarIcons = toolbarIcons, topBarIcons = topBarIcons ) { /* content */ } `} Initialization can also be done using default icons: 1. `com.devexperts.dxcharts.lib.ui.theme.icons.DefaultCommonIcons` 2. `com.devexperts.dxcharts.lib.ui.theme.icons.DefaultStudiesIcons` 3. `com.devexperts.dxcharts.lib.ui.theme.icons.DefaultToolbarIcons` 4. `com.devexperts.dxcharts.lib.ui.theme.icons.DefaultTopBarIcons` {` val topBarIcons = object : TopBarIcons { override val ArrowBack: ImageVector // Initialization with new icon resourse @Composable get() = ImageVector.vectorResource(id = R.drawable.ic_icon7) override val ArrowForward: ImageVector // Initialization with default icon @Composable get() = DefaultTopBarIcons.ArrowForward } DxChartsTheme( topBarIcons = topBarIcons ) { /* content */ } `} --- ## Chart for Android/Configuration/Theme configuration/Using a theme/Overview.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Using a theme To use the default chart theme, you don't need to pass any parameters other than the `DxChartsScreen` function as the '`content`' parameter: {` //... DxChartsTheme { DxChartsScreen(/* ... */) } //... `} To modify the sizes of UI elements, their paddings, colors, corners, and so on, you can adjust the parameters of `DxChartsTheme`: {` //... DxChartsTheme( colors = /*...*/, typography = /*...*/, dimensions = /*...*/, toolbarIcons = /*...*/, commonIcons = /*...*/, topBarIcons = /*...*/, studiesIcons = /*...*/, shapes = /*...*/ ) { DxChartsScreen(/* ... */) } //... `} --- ## Chart for Android/Configuration/Theme configuration/Using a theme/Shapes.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Text Styles Changing shapes is done by passing new parameters to `com.devexperts.dxcharts.lib.ui.theme.DxChartsShapes` and then passing the object to `DxChartsTheme`: {` val shapes = DxChartsShapes( instrumentListCheckedItemBackgroundShape = RoundedCornerShape(/* ... */) ) DxChartsTheme( shapes = shapes ) { /* content */ } `} All shapes used in the library can be found [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Components/Shapes) --- ## Chart for Android/Configuration/Theme configuration/Using a theme/Text Styles.mdx import { MDXCodeBlock } from '../../../../../mdx-config/code-block/MDXCodeBlock'; # Text Styles Changing text styles is done by passing new parameters to `com.devexperts.dxcharts.lib.ui.theme.DxChartsTypography` and then passing the object to `DxChartsTheme`: {` val typography = DxChartsTypography( aggregationSelectorNumKeyboard = TextStyle(/* ... */), hintCardTitle = DxChartsTypography().hintCardTitle.copy(/* ... */) ) DxChartsTheme( typography = typography ) { /* content */ } `} Or {` val typography = DxChartsTypography().copy( aggregationSelectorNumKeyboard = TextStyle(/* ... */), hintCardTitle = DxChartsTypography().hintCardTitle.copy(/* ... */) ) DxChartsTheme( typography = typography ) { /* content */ } `} All text styles used in the library can be found [here](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Configuration/Theme-configuration/Components/Text-Styles) --- ## Chart for Android/Integration Example.mdx import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; # Android DxCharts library integration example This page describes a short example of integrating a library into an application. For the full algorithm for connecting the DxCharts library, please check [Quick Start page](https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-Android/Quick-Start) ## Add dependencies First step is to add dependencies into your build.gradle file ``` plugins { //... id 'org.jetbrains.kotlin.android' version '1.9.0' apply false } android { //... buildFeatures { compose true } composeOptions { kotlinCompilerExtensionVersion = "1.5.2" } //... } dependencies { //Compose dependencies def composeBom = platform('androidx.compose:compose-bom:$COMPOSE_BOM_VERSION') implementation composeBom androidTestImplementation composeBom implementation 'androidx.compose.foundation:foundation' implementation 'androidx.compose.ui:ui' implementation 'androidx.activity:activity-compose:$ACTIVITY_COMPOSE_VERSION' implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0' implementation 'androidx.constraintlayout:constraintlayout-compose:1.1.0' implementation 'androidx.compose.material3:material3' //Additional dependencies that used in the library implementation 'com.google.code.gson:gson:2.10.1' // For JSON parsing implementation 'com.jakewharton.threetenabp:threetenabp:1.4.7' // For date and time API //Required DxCharts dependencies implementation 'com.devexperts.dxcharts.lib:dxcharts_lib:$MAIN_LIB_VERSION' implementation 'com.devexperts.dxcharts.provider:dxcharts_data_providers:$'DATA_PROVIDER_VERSION' //Not necessary dependencies. Default implementations of data providers with DxFeed API implementation 'com.devexperts.dxcharts.provider.candles:dxcharts_dxfeed_candles_provider:$CANDLES_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.events:dxcharts_dxfeed_events_provider:$EVENTS_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.ipf:dxcharts_dxfeed_ipf_provider:$IPF_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.news:dxcharts_dxfeed_news_provider:$NEWS_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.studies:dxcharts_studies_provider:$STUDIES_PROVIDER_VERSION' } ``` ## DxCharts configuration Next step will be creating DxCharts view. {` class DXChartsActivity : AppCompatActivity() { /** * Creating providers objects. * In this example we use our default providers implementations, but you can create your own. * Notice that default implementations based on the DxFeed API and require endpointAddress, url and token. */ private val quotesProvider = DxFeedQuotesProvider( endpointAddress = "ENDPOINT_ADDRESS" ) private val candlesProvider = DxFeedCandlesProvider( endpointAddress = "ENDPOINT_ADDRESS" ) private val newsProvider = DxFeedNewsProvider( url = "NEWS_URL", token = "Basic " + String(Base64.encode("NEWS_TOKEN".encodeToByteArray(),Base64.NO_WRAP)) ) private val dxFeedIpfProvider = DxFeedHttpIpfProvider( url = "IPF_URL", token = "Basic " + String(Base64.encode("IPF_TOKEN".encodeToByteArray(),Base64.NO_WRAP)) ) private val eventsProvider = DxFeedEventsProvider( url = "EVENTS_URL", token = "Basic " + String(Base64.encode("EVENTS_TOKEN".encodeToByteArray(), Base64.NO_WRAP)) ) private val studiesProvider = DxStudiesDataProviderImplementation() private val studiesPreviewProvider = DxStudiesPreviewDataProviderImplementation() private val scheduleProvider = DxFeedHttpScheduleProvider( token = "Basic " + String( Base64.encode( BuildConfig.IPF_TOKEN.encodeToByteArray(), Base64.NO_WRAP ) ) ) /** * Creating repositories objects. * Library provides 2 default variants of implementations of repositories. * 1. SerializableStorage repositories. They implement saving their inner state into shared preferences. To restore their state you need to use deserialize() method. * 2. LocalStorage repositories. They don't save any state across instances of chart and don't need context as a parameter. * Also you can create your own implementations of repositories. */ private val studiesRepository by lazy { /** * StudiesRepository needs indicator items to be passed. Default studies list can be obtained by using StudiesLoader().load(assets). */ SerializableStorageDefaultStudiesRepository(application, StudiesLoader().load(assets)) } private val aggregationsRepository by lazy { SerializableStorageDefaultAggregationsRepository(application) } private val colorsRepository by lazy { SerializableStorageDefaultColorsRepository(application) } private val drawingsLineWidthRepo by lazy { SerializableStorageDefaultDrawingsLineWidthRepo(application) } private val drawingsSnapshotsRepo by lazy { SerializableStorageDefaultDrawingsSnapshotsRepo(application) } private val drawingsFirstClickRepo by lazy { SerializableStorageDefaultFirstClickRepo(application) } private val drawingsTypesRepo by lazy { SerializableStorageDefaultDrawingsTypesRepo(application) } private val highlighterColorsRepository by lazy { SerializableStorageDefaultHighlighterColorsRepository(application) } private val instrumentRepository by lazy { SerializableStorageDefaultInstrumentRepository(application) } private val timeframePresetsRepository by lazy { SerializableStorageDefaultTimeframePresetsRepository(application) } private val ordersRepository by lazy { SerializableStorageDefaultOrdersRepository(application) } private val settingsRepository by lazy { SerializableStorageDefaultSettingsRepository(application) } private val studiesTemplatesRepository by lazy { SerializableStorageDefaultStudiesTemplatesRepository(application) } private var isSystemInDarkTheme: Boolean = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) /** * Enables or disables the trading buttons feature. By default it's true. */ settingsRepository.setTradingButtonsFeatureEnabled(isEnabled = false) isSystemInDarkTheme = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES /** * Method that restore state into SerializableStorage repositories. */ deserializeRepositories() /** * Initializing providers. */ quotesProvider.connect() candlesProvider.connect() dxFeedIpfProvider.connect() studiesProvider.connect() studiesPreviewProvider.connect() /** * Handling errors in default repositories. * Default providers have errorFlow that let you set your own way of handling errors. * It's optional and can be customized depending on your needs. */ lifecycleScope.launch { merge( candlesProvider.errorFlow, quotesProvider.errorFlow, dxFeedIpfProvider.errorFlow, eventsProvider.errorFlow, newsProvider.errorFlow, studiesProvider.errorFlow, studiesPreviewProvider.errorFlow ).collect { error -> // Custom error handling logic val errorClassName = error?.javaClass?.simpleName val parentClassName = error?.javaClass?.superclass?.simpleName Log.e("DxChartsProviderError", "$errorClassName from $parentClassName : $error?.message") if(error?.error != null){ throw error.error!! } } } setContent { /** * Get initial theme and from settings repository and set according color palette for DxCharts */ val initialTheme = getPalette(settingsRepository.getCurrentTheme()) var theme by remember { mutableStateOf(initialTheme) } /** * Get initial instrument from settings repository */ val initialInstrument = instrumentRepository.getSelectedInstrument() /** * Initializing DxChart */ DxChartsTheme( colors = theme ) { Surface( modifier = Modifier.fillMaxSize(), color = DxChartsTheme.colors.mainChartBg ) { DxChartsScreen( DxChartsConfig( /** * Passing repositories to the DxChartConfig. * By default all repositories are LocalStorage and don't save their state across instances. */ repositories = DxChartsConfig.Repositories( studiesRepository = studiesRepository, aggregationsRepository = aggregationsRepository, colorsRepository = colorsRepository, drawingsLineWidthRepo = drawingsLineWidthRepo, drawingsSnapshotsRepo = drawingsSnapshotsRepo, drawingsFirstClickRepo = drawingsFirstClickRepo, drawingsTypesRepo = drawingsTypesRepo, highlighterColorsRepository = highlighterColorsRepository, instrumentRepository = instrumentRepository, ordersRepository = ordersRepository, timeframePresetsRepository = timeframePresetsRepository, settingsRepository = settingsRepository, studiesTemplatesRepository = studiesTemplatesRepository ), /** * Passing providers to the DxChartConfig. * By default all providers are null */ providers = DxChartsDataProviders( candlesProvider = candlesProvider, ipfProvider = dxFeedIpfProvider, scheduleProvider = scheduleProvider, quotesProvider = quotesProvider, eventsProvider = eventsProvider, newsProvider = newsProvider, studiesDataProvider = studiesProvider, studiesDataPreviewProvider = studiesPreviewProvider ), /** * Set initial instrument that will be used if there are no saved instruments in the instrument repository. */ initialInstrument = instrument ), /** * Callback for click on Buy button. */ buyClicked = {}, /** * Callback for click on Sell button. */ sellClicked = {}, /** * Callback for changing theme in the DxCharts settings. */ themeChanged = { theme = getPalette(it) }, /** * Setting your logo. * By default it's null. */ logo = Logo( image = R.drawable.devexperts_logo, position = Logo.LogoPosition.BOTTOM_LEFT, ), /** * Whether to show toolbar or not. */ showToolbar = true, ) } } } } /** * Disconnecting default providers. */ override fun onDestroy() { if (!isChangingConfigurations) { newsProvider.disconnect() dxFeedIpfProvider.disconnect() quotesProvider.disconnect() candlesProvider.disconnect() eventsProvider.disconnect() studiesProvider.disconnect() studiesPreviewProvider.disconnect() } } /** * Function that checks what color palette to use. */ private fun getPalette(theme: Theme): DxChartsColors { return when (theme) { Theme.AUTO -> { if (isSystemInDarkTheme) darkPalette() else lightPalette() } Theme.LIGHT -> { lightPalette() } Theme.DARK -> { darkPalette() } } } /** * Function that responsible for restoring state of SerializableStorage repositories. */ private fun deserializeRepositories() { val prefs = application.getSharedPreferences("dxcharts_demo_app", MODE_PRIVATE) val aggregations = prefs.getString("aggregations_repo", null) val colors = prefs.getString("colors_repo", null) val drawingsLine = prefs.getString("drawings_line_repo", null) val drawingsSnapshots = prefs.getString("drawings_snapshots_repo", null) val drawingsTypes = prefs.getString("drawings_types_repo", null) val drawingsFirstClick = prefs.getString("drawings_first_click_repo", null) val highlighter = prefs.getString("highlighter_repo", null) val instrument = prefs.getString("instrument_repo", null) val orders = prefs.getString("orders_repo", null) val settings = prefs.getString("settings_repo", null) val studies = prefs.getString("studies_repo", null) val studiesTemplates = prefs.getString("studies_templates_repo", null) val timeframePresets = prefs.getString("timeframe_presets_repo", null) aggregations?.let { aggregationsRepository.deserialize(it) } colors?.let { colorsRepository.deserialize(it) } drawingsLine?.let { drawingsLineWidthRepo.deserialize(it) } drawingsSnapshots?.let { drawingsSnapshotsRepo.deserialize(it) } drawingsTypes?.let { drawingsTypesRepo.deserialize(it) } drawingsFirstClick?.let { drawingsFirstClickRepo.deserialize(it) } highlighter?.let { highlighterColorsRepository.deserialize(it) } instrument?.let { instrumentRepository.deserialize(it) } orders?.let { ordersRepository.deserialize(it) } settings?.let { settingsRepository.deserialize(it) } studies?.let { studiesRepository.deserialize(it) } studiesTemplates?.let { studiesTemplatesRepository.deserialize(it) } timeframePresets?.let { timeframePresetsRepository.deserialize(it) } } } `} --- ## Chart for Android/Quick Start.mdx import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; # Quick start The documentation describes the algorithm for connecting the DxCharts library for the Android OS with basic functionality. The documentation is divided into two parts: 1. Dependency setup and Gradle configuration; 2. Connecting the library to the App, configuring the Activity. ## Dependencies and Gradle configuration ### WebView support Our framework use WebView to show chart. Minimal supported version of WebView is **`83.0.4103.106`**. ### Jetpack Compose setup To work with the library, it is necessary to import dependencies and configure Jetpack Compose. You can refer to the official manual for configuring Compose at https://developer.android.com/jetpack/compose/setup. Library support all versions of **`kotlinCompilerExtensionVersion`** from 1.5.0 and higher and **`Compose BOM`** versions from 2023.08.00 According to requirement of **`kotlinCompilerExtensionVersion`** you also need kotlin version 1.9.0 and higher. The **`Android Gradle Plugin version`** must be 8.1.0 or higher. ``` plugins { //... id 'com.android.library' version '8.1.0' apply false id 'org.jetbrains.kotlin.android' version '1.9.0' apply false } ``` ``` dependencies { //... def composeBom = platform('androidx.compose:compose-bom:$COMPOSE_BOM_VERSION') implementation composeBom androidTestImplementation composeBom implementation 'androidx.compose.foundation:foundation' implementation 'androidx.compose.ui:ui' implementation 'androidx.activity:activity-compose:$ACTIVITY_COMPOSE_VERSION' //... } ``` ``` android { //... buildFeatures { compose true } composeOptions { kotlinCompilerExtensionVersion = "1.5.2" } //... } ``` Also in the library we use **`viewModels`**, **`constraintLayout`**, **`material3`**. So you need to add dependencies for them: ``` dependencies { //... implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0' implementation 'androidx.constraintlayout:constraintlayout-compose:1.1.0' implementation 'androidx.compose.material3:material3' //... } ``` ### Library's repository and Nexus authorization All artifacts of the DxCharts library for Android are located in the Maven repository https://nexus.in.devexperts.com/. To access it, you need to add the following repository to your project: https://nexus.in.devexperts.com/repository/dx/. Also, to access the artifacts, authorization in the Maven repository is required. For more details, you can refer to the following links: [Maven authentication for Gradle](https://confluence.in.devexperts.com/display/MOB/Maven+authentication+for+Gradle), [CI / CD best practices in Devexperts](https://confluence.in.devexperts.com/pages/viewpage.action?pageId=336334267). ``` buildscript { repositories { maven { url 'https://nexus.in.devexperts.com/repository/dx/' } mavenCentral() } dependencies { classpath 'com.devexperts.gradle.auth:maven-auth:1.0' } } apply plugin: 'maven-auth' } ``` ### Other dependencies requirements The library uses the **`Gson`** library for parsing JSON data and **`ThreeTenABP`** for working with dates and times. You need to add these dependencies to your project: ``` dependencies { //... implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.jakewharton.threetenabp:threetenabp:1.4.7' //... } ``` ### Library's modules The library functionality requires the connection of the core module and data providers. You can use the standard demo implementation of data providers or implement your own using the provided interfaces. For implementing custom data providers, no additional dependencies are required. More details can be found in the guide Configuration. #### Core DxCharts Android Library module The core package of the library includes fundamental logic and UI components. **Version (upd. 2 Jun 2025) - 1.4.5** **Nexus URL -** https://nexus.in.devexperts.com/#browse/search=keyword%3Dcom.devexperts.dxcharts.lib%3Adxcharts_lib **Minimum Android API version - 23** Dependency connection in **build.gradle**: ``` dependencies { implementation 'com.devexperts.dxcharts.lib:dxcharts_lib:$MAIN_LIB_VERSION' implementation 'com.devexperts.dxcharts.provider:dxcharts_data_providers:$'DATA_PROVIDER_VERSION' // User's dependencies } ``` #### DxFeed Data providers for DxCharts Android Library A set of standard Demo-implementations for data providers (with a 15-minute delay), operating through the **DxFeed API**. | Module name | Description | Version(upd. 2 Jun 2025) | Nexus Link | Package name in build.gradle | Min. API Version | |----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|------------------| | dxcharts_dxfeed_candles_provider | Provides data with selected trading instrument's candles that needs to be displayed on the chart. | 1.4.5 | [Link](https://nexus.in.devexperts.com/#browse/search=keyword%3Dcom.devexperts.dxcharts.provider.candles%3Adxcharts_dxfeed_candles_provider) | `com.devexperts.dxcharts.provider.candles:dxcharts_dxfeed_candles_provider ` | 24 | | dxcharts_dxfeed_events_provider | Provides data with events related to the selected trading instrument, including corporate events, dividends information, and splits. | 1.4.5 | [Link](https://nexus.in.devexperts.com/#browse/search=keyword%3Dcom.devexperts.dxcharts.provider.events%3Adxcharts_dxfeed_events_provider) | `com.devexperts.dxcharts.provider.events:dxcharts_dxfeed_events_provider` | 23 | | dxcharts_dxfeed_ipf_provider | Provides data with available trading instruments and trading hours. | 1.4.5 | [Link](https://nexus.in.devexperts.com/#browse/search=keyword%3Ddxcharts_dxfeed_ipf_provider) | `com.devexperts.dxcharts.provider.ipf:dxcharts_dxfeed_ipf_provider ` | 23 | | dxcharts_dxfeed_news_provider | Provides news about the selected instrument, which will be displayed on the chart. | 1.4.5 | [Link](https://nexus.in.devexperts.com/#browse/search=keyword%3Ddxcharts_dxfeed_news_provider) | `com.devexperts.dxcharts.provider.news:dxcharts_dxfeed_news_provider ` | 23 | | dxcharts_studies_provider | Calculates and provides indicators data on the chart based on the provided input data. | 1.4.5 | [Link](https://nexus.in.devexperts.com/#browse/search=keyword%3Ddxcharts_dxfeed_studies_provider) | `com.devexperts.dxcharts.provider.studies:dxcharts_dxfeed_studies_provider ` | 24 | All of this providers are not necessary. It is possible to not connect one or several data providers to the core module of the library. In this case, the library will still function, but the data provided by these provider(s) will not be shown. To enable all functionality, all default data providers will be used in this guide. Dependencies in **build.gradle**: ``` dependencies { implementation 'com.devexperts.dxcharts.provider.candles:dxcharts_dxfeed_candles_provider:$CANDLES_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.events:dxcharts_dxfeed_events_provider:$EVENTS_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.ipf:dxcharts_dxfeed_ipf_provider:$IPF_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.news:dxcharts_dxfeed_news_provider:$NEWS_PROVIDER_VERSION' implementation 'com.devexperts.dxcharts.provider.studies:dxcharts_studies_provider:$STUDIES_PROVIDER_VERSION' // User's dependencies } ``` ## Connecting library to the App In this section, the algorithm for connecting the DxCharts library directly to the Android application is described. The data obtained by the library consists of demo data from the **DxFeed API**. All modules from section 1 should be connected to the project. Upon application restart, the library parameters are reset to default. You can manually implement data repositories to store data in the persistent storage (see Configuration **article 4**). ### Algorithm for Connecting the Library #### Data provider initialization The first step is to initialize objects for all data providers connected to the library. Data providers should be initialized once during the program's execution. You can do this anywhere in your project. In our example, this occurs inside the base Activity class. {` class DXChartsActivity : AppCompatActivity() { //... private val quotesProvider: DxFeedQuotesProvider = DxFeedQuotesProvider() private val candlesProvider = DxFeedCandlesProvider() private val newsProvider = DxFeedNewsProvider() private val dxFeedIpfProvider = DxFeedHttpIpfProvider() private val scheduleProvider = DxFeedHttpScheduleProvider() private val eventsProvider = DxFeedEventsProvider() private val studiesProvider = DxStudiesDataProviderImplementation() private val studiesPreviewProvider = DxStudiesPreviewDataProviderImplementation() //... } `} Default implementations of candles and quotes providers requires endpoint address for dxfeed library as a parameter. Default implementations of news, instruments and events providers requires url and token for authorization on dxfeed sources. If you don't pass any parameter it will be empty and corresponding provider won't work {` private val quotesProvider = DxFeedQuotesProvider( endpointAddress = "endpoint" ) private val candlesProvider = DxFeedCandlesProvider( endpointAddress = "endpoint" ) private val newsProvider = DxFeedNewsProvider( url = "https://url", token = "Basic " + String(Base64.encode("token".encodeToByteArray(), Base64.NO_WRAP)) ) private val dxFeedIpfProvider = DxFeedHttpIpfProvider( url = "https://url", token = "Basic " + String(Base64.encode("token".encodeToByteArray(), Base64.NO_WRAP)) ) private val scheduleProvider = DxFeedHttpScheduleProvider( token = "Basic " + String(Base64.encode("token".encodeToByteArray(), Base64.NO_WRAP)) ) private val eventsProvider = DxFeedEventsProvider( url = "https://url", token = "Basic " + String(Base64.encode("token".encodeToByteArray(), Base64.NO_WRAP)) ) `} #### Start of Data providers work, connecting to the Dxfeed API The next step is to invoke methods of the created objects to establish a connection to the DxFeed API and start fetching data. In our example, this takes place in the **`onCreate`** method of the base Activity to create a new connection when the Activity is recreated and to close it in the **`onDestroy`** method. {` //... override fun onCreate(savedInstanceState: Bundle?) { //... quotesProvider.connect() candlesProvider.connect() dxFeedIpfProvider.requestData() studiesProvider.start() studiesPreviewProvider.start() //... } //... `} #### Loading the list of indicators The list of indicators is loaded into the library manually. To load the list of default indicators from XML files stored in the library, you need to override the default repository for them as follows: {` //... val studiesRepo = LocalStorageDefaultStudiesRepository( StudiesLoader().load(assets).toMutableList() ) studiesRepo.initialiseDefaultStudies() //... `} #### Creating the library configuration object The next step is creating an object of the **`com.devexperts.dxcharts.lib.domain.DxChartsConfig`** class, which holds the initial configuration of the DxCharts library. It includes data providers, repositories for data storage, and the default instrument name to be loaded onto the chart. All repositories for data storage are necessary for the library to function. By default, they do not persist data in the device's memory and are cleared upon application restart. All default data providers are initially set to `null`. When creating the configuration, we override them with the demo data providers that we connected earlier. {` //... override fun onCreate(savedInstanceState: Bundle?) { //... val config = DxChartsConfig( repositories = DxChartsConfig.Repositories( studiesRepository = studiesRepo, ), providers = DxChartsDataProviders( candlesProvider = candlesProvider, ipfProvider = dxFeedIpfProvider, scheduleProvider = scheduleProvider, quotesProvider = quotesProvider, eventsProvider = eventsProvider, newsProvider = newsProvider, studiesDataProvider = studiesProvider, studiesDataPreviewProvider = studiesPreviewProvider ), initialInstrumentName = InstrumentData.NONE, ) //... } //... `} #### Show DxCharts Android Library content To connect DxCharts library to the application and display its UI, you need to call the method **`androidx.activity.compose.setContent`**. Inside this method, pass the theme used by the library (`**com.devexperts.dxcharts.lib.ui.theme.DxChartsTheme**)` in which the entry point into the DxCharts library is called. All parameters of the **`DxChartsTheme`** theme can be modified by a developer, for more details - Configuration **article 2**. In addition to passing the configuration (**`dxChartsConfig`**) when calling the **`com.devexperts.dxcharts.lib.ui.DxChartsScreen`** method, you can also provide callbacks for: 1. Pressing the "back" button on the chart screen - **`backHandler`** 2. Pressing the "buy" button on the trading widget - **`buyClicked`** 3. Pressing the "sell" button on the trading widget - **`sellClicked`** By default, they are set to `null`, meaning that nothing will happen when the buttons are pressed, and the application cannot be closed by pressing the "back" button. {` //... override fun onCreate(savedInstanceState: Bundle?) { //... setContent { DxChartsTheme { DxChartsScreen( dxChartsConfig = config, backHandler = { finish() } ) } } //... } //... `} #### Customizing size of the chart Chart can be placed on only part of the screen, so that the rest is occupied by your UI. To do that you can wrap it in container and set size or position for it. {` setContent { DxChartsTheme { Surface( modifier = Modifier .size( height = 600.dp, width = 300.dp ), ) { DxChartsScreen( //... ) } } } `} We do not recommend setting the height less than **`600dp`** and the width less than **`300dp`**, if you want to use full UI of our framework (toolbar, trade buttons, etc.). Otherwise, UI artifacts may occur. #### Providing your logo With calling **`com.devexperts.dxcharts.lib.ui.DxChartsScreen`** method, you also can provide your logo to chart. By default, there are no any logo. All you need is to pass **`com.devexperts.dxcharts.lib.domain.Logo`** object. As image you provide you logo from resources. {` setContent { DxChartsTheme { DxChartsScreen( logo = Logo( image = R.drawable.devexperts_logo, position = Logo.LogoPosition.BOTTOM_LEFT, ), ) } } `} As position you just need to define corner of the chart, where logo should be shown. {` enum class LogoPosition { /** * Logo positioned at the top right corner of the screen. */ TOP_RIGHT, /** * Logo positioned at the top left corner of the screen. */ TOP_LEFT, /** * Logo positioned at the bottom right corner of the screen. */ BOTTOM_RIGHT, /** * Logo positioned at the bottom left corner of the screen (default position). */ BOTTOM_LEFT } `} #### Configuring toolbar Also you can define whether you want to use toolbar or not, by passing boolean to `showToolbar` parameter. By default it is set to `true` {` setContent { DxChartsTheme { DxChartsScreen( showToolbar = true, ) } } `} #### Configuring onDestroy method To ensure that providers disconnect from the DxFeed API when the Activity is destroyed, preventing memory leaks, you need to manually invoke the corresponding methods for all providers in the `onDestroy` method: {` //... override fun onDestroy() { //... newsProvider.disconnect() dxFeedIpfProvider.disconnect() quotesProvider.disconnect() candlesProvider.disconnect() eventsProvider.disconnect() studiesProvider.disconnect() studiesPreviewProvider.disconnect() //... } //... `} --- ## Chart for iOS/Configuration/Customization/Chart theme.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; ## Chart theme Now you can set your own dark and light theme colors for chart. To set your own values for chart theme you can parse json file to Theme.self object or just provide bundles for your `paletteDark.json` and/or `paletteLight.json` : {` // Example: configure custom source DXChartApp.resourceManager = DXCResourceManager(darkPaletteBundle: .darkStylePaletteBundle, lightPaletteBundle: .lightPaletteBundle) `} The example of json: {` {   "crossTool": {     "lineColor": "rgba(255,170,0,1)",     "labelTextColor": "rgba(255,170,0,1)",     "labelBoxColor": "rgba(1,1,1,1)"   },   "candleTheme": {     "upColor": "rgba(77,153,83,1)",     "downColor": "rgba(217,44,64,1)",     "noneColor": "rgba(255,255,255,1)",     "upWickColor": "rgba(77,153,83,1)",     "downWickColor": "rgba(217,44,64,1)",     "noneWickColor": "rgba(255,255,255,1)"   },   "barTheme": {     "upColor": "rgba(77,153,83,1)",     "downColor": "rgba(217,44,64,1)",     "noneColor": "rgba(255,255,255,1)"   },   "equivolumeTheme": {     "upColor": "rgba(77,153,83,1)",     "downColor": "rgba(217,44,64,1)",     "noneColor": "rgba(255,255,255,1)"   },   "lineTheme": {     "upColor": "rgba(77,153,83,1)",     "downColor": "rgba(217,44,64,1)",     "noneColor": "rgba(255,255,255,1)"   },   "chartAreaTheme": {     "backgroundColor": "rgba(115,115,115,1)",     "axisColor": "rgba(128,128,128,1)",     "gridColor": "rgba(40,40,40,1)"   },   "scatterPlot": {     "mainColor": "rgba(255,255,255,1)"   },   "areaTheme": {     "lineColor": "rgba(127,120,214,1)",     "startColor": "rgba(169,38,251,0.4)",     "stopColor": "rgba(169,38,251,0.1)"   },   "baseLineTheme": {     "lowerSectionStrokeColor": "rgba(217,44,64,1)",     "upperSectionStrokeColor": "rgba(77,153,83,1)",     "lowerSectionFillColor": "rgba(217, 44, 64, 0.07)",     "upperSectionFillColor": "rgba(77, 153, 83, 0.07)",   },   "histogram": {     "upCap": "rgba(77,153,83,1)",     "upBottom": "rgba(77,153,83,0.1)",     "upBright": "rgba(77,153,83,0.4)",     "downCap": "rgba(217,44,64,1)",     "downBottom": "rgba(217,44,64,0.1)",     "downBright": "rgba(217,44,64,0.4)",     "noneCap": "rgba(255,255,255,1)",     "noneBottom": "rgba(255,255,255,0.1)",     "noneBright": "rgba(255,255,255,0.4)"   },   "waterMarkTheme": {     "firstRowColor": "rgba(255,255,255,0.2)",     "secondRowColor": "rgba(255,255,255,0.2)",     "thirdRowColor": "rgba(255,255,255,0.2)"   },   "highlights": {     "NO_TRADING": {       "border": "rgba(107,96,86,1)",       "background": "transparent",       "label": "transparent"     },     "AFTER_MARKET": {       "border": "rgba(107,96,86,1)",       "background": "rgba(255, 170, 0, 0.07)",       "label": "transparent"     },     "PRE_MARKET": {       "border": "rgba(107,96,86,1)",       "background": "rgba(38, 251, 149, 0.07)",       "label": "transparent"     },     "REGULAR": {       "border": "rgba(107,96,86,1)",       "background": "transparent",       "label": "transparent"     }   },   "activeCandleTheme": {     "upColor": "rgba(98,201,93,1)",     "downColor": "rgba(255,47,47,1)",     "noneColor": "rgba(255,255,255,1)",     "upWickColor": "rgba(98,201,93,1)",     "downWickColor": "rgba(255,47,47,1)",     "noneWickColor": "rgba(255,255,255,1)"   },   "volume": {     "downCapColor": "rgba(99,30,37,1)",     "upCapColor": "rgba(42,72,44,1)",     "noneBarColor": "rgba(255,255,255,0.4)",     "downBarColor": "rgba(42,72,44,1)",     "upBarColor": "rgba(99,30,37,1)",     "noneCapColor": "rgba(255,255,255,0.4)"   },   "highLowTheme": {     "highColor": "rgba(223,222,223,1)",     "lowColor": "rgba(223,222,223,1)"   },   "emptyChartTheme": {     "noDataColor": "rgba(255, 255, 255, 0.15)"   },   "instrumentInfo": {     "textColor": "#aeb1b3"   },   "paneResizer": {     "lineColor": "rgba(55,55,54,1)",     "bgColor": "rgba(20,20,19,1)",     "bgHoverColor": "rgba(55,55,54,0.6)"   },   "events": {     "earnings": {       "color": "rgba(217,44,64,1)"     },     "dividends": {       "color": "rgba(169,38,251,1)"     },     "splits": {       "color": "rgba(244,187,63,1)"     },     "conference-calls": {       "color": "rgba(48,194,97,1)"     }   },   "secondaryChartTheme": [     {       "lineTheme": {         "bullColor": "rgba(226,61,25,1)",         "bearColor": "rgba(226,61,25,1)",         "noneColor": "rgba(226,61,25,1)"       },       "areaTheme": {         "lineColor": "rgba(226,61,25,1)",         "startColor": "rgba(226,61,25,0.8)",         "stopColor": "rgba(226,61,25,0)"       }     },     {       "lineTheme": {         "bullColor": "rgba(250,191,64,1)",         "bearColor": "rgba(250,191,64,1)",         "noneColor": "rgba(250,191,64,1)"       },       "areaTheme": {         "lineColor": "rgba(250,191,64,1)",         "startColor": "rgba(250,191,64,0.8)",         "stopColor": "rgba(250,191,64,0)"       }     },     {       "lineTheme": {         "bullColor": "rgba(169,38,251,1)",         "bearColor": "rgba(169,38,251,1)",         "noneColor": "rgba(169,38,251,1)"       },       "areaTheme": {         "lineColor": "rgba(169,38,251,1)",         "startColor": "rgba(169,38,251,0.8)",         "stopColor": "rgba(169,38,251,0)"       }     },     {       "lineTheme": {         "bullColor": "rgba(77,211,240,1)",         "bearColor": "rgba(77,211,240,1)",         "noneColor": "rgba(77,211,240,1)"       },       "areaTheme": {         "lineColor": "rgba(77,211,240,1)",         "startColor": "rgba(77,211,240,0.8)",         "stopColor": "rgba(77,211,240,0)"       }     },     {       "lineTheme": {         "bullColor": "rgba(59,203,91,1)",         "bearColor": "rgba(59,203,91,1)",         "noneColor": "rgba(59,203,91,1)"       },       "areaTheme": {         "lineColor": "rgba(59,203,91,1)",         "startColor": "rgba(59,203,91,0.8)",         "stopColor": "rgba(59,203,91,0)"       }     }   ],   "yAxis": {     "backgroundColor": "rgba(1,1,1,1)",     "backgroundHoverColor": "rgba(20,20,19,1)",     "labelBoxColor": "rgba(20,20,19,1)",     "labelDescriptionFillColor": "rgba(20,20,19,1)",     "labelTextColor": "rgba(128,128,128,1)",     "labelInvertedTextColor": "rgba(20,20,19,1)",     "rectLabelTextColor": "rgba(255,255,255,1)",     "rectLabelInvertedTextColor": "rgba(20,20,19,1)",     "zeroPercentLine": "rgba(55,55,54,1)"   },   "labels": {     "lastPrice": {       "textNegative": "rgba(255,255,255,1)",       "textPositive": "rgba(255,255,255,1)",       "textSelected": "rgba(0,0,0,1)",       "boxNegative": "rgba(217,44,64,1)",       "boxPositive": "rgba(77,153,83,1)",       "boxSelected": "rgba(255,255,255,1)"     },     "countdownToBarClose": {       "textNegative": "rgba(255,255,255,1)",       "textPositive": "rgba(255,255,255,1)",       "textSelected": "rgba(255,255,255,1)",       "boxNegative": "rgba(217,44,64,1)",       "boxPositive": "rgba(77,153,83,1)",       "boxSelected": "rgba(255,255,255,1)"     },     "highLow": {       "high": {         "boxColor": "rgba(107,96,86,1)",         "textColor": "rgba(255,255,255,1)",         "descriptionText": "High"       },       "low": {         "boxColor": "rgba(107,96,86,1)",         "textColor": "rgba(255,255,255,1)",         "descriptionText": "Low"       }     },     "bidAsk": {       "bid": {         "boxColor": "rgba(77,153,83,1)",         "textColor": "rgba(255,255,255,1)",         "descriptionText": "Bid"       },       "ask": {         "boxColor": "rgba(217,44,64,1)",         "textColor": "rgba(255,255,255,1)",         "descriptionText": "Ask"       }     },     "prePostMarket": {       "post": {         "boxColor": "rgba(38,251,149,1)",         "textColor": "rgba(20,20,19,1)",         "descriptionText": "Post"       },       "pre": {         "boxColor": "rgba(255,170,0,1)",         "textColor": "rgba(20,20,19,1)",         "descriptionText": "Pre"       }     },     "prevDayClose": {       "boxColor": "rgba(107,96,86,1)",       "textColor": "rgba(255,255,255,1)"     }   },   "xAxis": {     "backgroundColor": "rgba(1,1,1,1)",     "labelBoxColor": "rgba(20,20,19,1)",     "labelTextColor": "rgba(128,128,128,1)"   },   "navigationMap": {     "backgroundColor": "rgba(1,1,1,1)",     "buttonColor": "rgba(255,255,255,0.1)",     "buttonArrowColor": "rgba(212,212,211,1)",     "knotColor": "rgba(255,255,255,0.1)",     "knotLineColor": "rgba(212,212,211,1)",     "sliderColor": "rgba(255,255,255,0.08)",     "knotBorderColor": "#0b0d1a",     "timeLabelsTextColor": "rgba(128,128,128,1)"   },   "drawingsTheme": {     "points": {       "defaultKeyPoint": {         "shape": "circle",         "radius": 4.5,         "fillStyle": "rgba(255,170,0,1)",         "lineWidth": 0,         "lineColor": "rgba(20,20,19,1)",         "opacity": 1       },       "activeKeyPoint": {         "shape": "circle",         "radius": 4.5,         "fillStyle": "rgba(255,170,0,1)",         "lineWidth": 0,         "lineColor": "rgba(20,20,19,1)",         "opacity": 1       },       "defaultHoverKeyPoint": {         "shape": "circle",         "radius": 4.5,         "fillStyle": "rgba(255,170,0,1)",         "lineWidth": 0,         "lineColor": "rgba(20,20,19,1)",         "opacity": 1       },       "activeHoverKeyPoint": {         "shape": "circle",         "radius": 4.5,         "fillStyle": "rgba(255,170,0,1)",         "lineWidth": 0,         "lineColor": "rgba(20,20,19,1)",         "opacity": 1       },       "addingKeyPoint": {         "shape": "circle",         "radius": 4.5,         "fillStyle": "rgba(255,170,0,1)",         "lineWidth": 0,         "lineColor": "rgba(20,20,19,1)",         "opacity": 1,         "radiusExtension": 12       }     },     "xAxis": {       "labelColor": "rgba(255,170,0,1)",       "highlightColor": "rgba(255,170,0,0.1)"     },     "yAxis": {       "labelColor": "rgba(255,170,0,1)",       "highlightColor": "rgba(255,170,0,0.1)"     },     "textBg": "rgba(20,20,19,0.7)",     "textColor": "rgba(255,170,0,1)"   } } `} Example of usage: {` DXCUserDefaultsManager.shared.chartThemeDark = self.parseTheme() func parseTheme() -> Theme? { guard let url = Bundle.main.url(forResource: "paletteDark", withExtension: "json") else { return nil } do { let jsonData = try Data(contentsOf: url) do { let decoder = JSONDecoder() let theme = try decoder.decode(Theme.self, from: jsonData) return theme } catch { debugPrint("Error decoding theme from JSON: \(error)") return nil } } catch { debugPrint("Error loading JSON data from file: \(error)") return nil } } `} --- ## Chart for iOS/Configuration/Customization/Custom Colors and Images.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; ## Custom Colors and Images You can provide your own Images and Colors for components. Just provide bundles for your `Images.xcassets` and/or `Colors.xcassets` : {` // Example: configure custom source DXChartApp.resourceManager = DXCResourceManager(colorBundle: .main, imageBundle: .main) `} **Note:** Provide dark and light images/colors to see expected result for these themes. Your `Images.xcassets` and/or `Colors.xcassets` should contain following names for images/colors: {` internal enum DXImages: String {     case accentColor = "AccentColor"     case arc = "Arc"     case callout = "Callout"     case curve = "Curve"     case cycle = "Cycle"     case dateAndPriceRange = "Date and price range"     case dateRange = "Date range"     case elliottImpulse = "Elliott impulse"     case elliottCorrection = "ElliottCorrection"     case extendedLine = "Extended line"     case fibArcs = "Fib arcs"     case fibChannel = "Fib channel"     case fibCircles = "Fib circles"     case fibExtension = "Fib extension"     case fibRays = "Fib rays"     case fibRetracements = "Fib retracements"     case fibSpiral = "Fib spiral"     case fibTimeExtension = "Fib time extension"     case fibTimeRatios = "Fib time ratios"     case fibTimezone = "Fib timezone"     case gannBox = "Gann box"     case gannFan = "Gann fan"     case gannSquare = "Gann square"     case highlighter = "Highlighter"     case horizontalRay = "Horizontal ray"     case icon = "Icon"     case infoLine = "Info line"     case oval = "Oval"     case path = "Path"     case pitchfork = "Pitchfork"     case plus = "Plus"     case priceLabel = "Price label"     case priceLine = "Price line"     case priceRange = "Price range"     case rrLong = "RR long"     case rrShort = "RR short"     case ray = "Ray"     case rectangle = "Rectangle"     case regressionTrend = "Regression trend"     case textFormating = "Text formating"     case timeLine = "Time line"     case trendChannel = "Trend channel"     case trendLine = "Trend line"     case vwap = "VWAP"     case xabd = "XABD"     case addIndicator = "addIndicator"     case arrow = "arrow"     case arrowDown = "arrowDown"     case arrowUp = "arrowUp"     case backButtonSettings = "backButtonSettings"     case bin = "bin"     case brush = "brush"     case candles = "candles"     case colorPicker = "colorPicker"     case colorSettings = "colorSettings"     case colorThumb = "colorThumb"     case colorTick = "colorTick"     case delete = "delete"     case dxRemove = "dxRemove"     case favourite = "favourite"     case favouriteFill = "favouriteFill"     case fidelityLogo = "fidelityLogo"     case lineType = "lineType"     case lineWidth = "lineWidth"     case logoSample = "logoSample"     case magnedMode = "magnedMode"     case pause = "pause"     case pencil = "pencil"     case plusButton = "plusButton"     case redoDrawing = "redoDrawing"     case removeIndicator = "removeIndicator"     case rightChevron = "rightChevron"     case search = "search"     case settings = "settings"     case sliderThumb = "sliderThumb"     case studies = "studies"     case text = "text"     case tick = "tick"     case timezoneChevron = "timezoneChevron"     case undoDrawing = "undoDrawing"     case chessboard = "Chessboard" } internal enum DXColors: String {     case askButtonBg = "ask-button-bg"     case askTextColor = "ask-text-color"     case bidButtonBg = "bid-button-bg"     case bidTextColor = "bid-text-color"     case buttonFocusBorder = "button-focus-border"     case buttonPrimaryBorderDefault = "button-primary-border-default"     case buttonPrimaryDefaultText = "button-primary-default-text"     case buyHoverBg = "buy-hover-bg"     case colorButtonBorder = "color_button_border"     case colorPaletteSetEight3 = "color_palette-set_eight-3"     case colorPaletteSetEleven3 = "color_palette-set_eleven-3"     case colorPaletteSetFive3 = "color_palette-set_five-3"     case colorPaletteSetFour3 = "color_palette-set_four-3"     case colorPaletteSetNine3 = "color_palette-set_nine-3"     case colorPaletteSetNine4 = "color_palette-set_nine-4"     case colorPaletteSetSeven3 = "color_palette-set_seven-3"     case colorPaletteSetThree3 = "color_palette-set_three-3"     case colorPaletteSetTwo3 = "color_palette-set_two-3"     case conferenceCallColor = "conferenceCall_color"     case dividendsColor = "dividends_color"     case drawingHandleNegativeBg = "drawing-handle-negative-bg"     case drawingHandlePositiveBg = "drawing-handle-positive-bg"     case drawingIconBorderBg = "drawing-icon_border-bg"     case dropdownDescriptionText = "dropdown-description-text"     case dropdownListItemDefaultText = "dropdown-list_item-default-text"     case dropdownListItemHoveredBg = "dropdown-list_item-hovered-bg"     case dropdownListItemSelectedText = "dropdown-list_item-selected-text"     case dxClearColor = "dxClearColor"     case earningColor = "earning_color"     case iconActiveBg = "icon-active-bg"     case iconActiveDisabledDefaultBg = "icon-active-disabled-default-bg"     case iconDisabledDefaultBg = "icon-disabled-default-bg"     case iconPrimaryDefaultBg = "icon-primary-default-bg"     case iconSecondaryDefaultBg = "icon-secondary-default-bg"     case inputDisabledText = "input-disabled-text"     case keyboardBg = "keyboard-bg"     case mButtonPrimaryDefault = "m-button-primary-default"     case mButtonDisabled = "m-button_disabled"     case mDropdownDefaultBg = "m-dropdown-default-bg"     case mNotificationBodyText = "m-notification_body-text"     case mNotificationHeaderText = "m-notification_header-text"     case mSeparator = "m-separator"     case mainChartBg = "main-chart-bg"     case mainChartValueText = "main_chart-value-text"     case newsBg = "newsBg"     case splitColor = "split_color"     case suspendedText = "suspended_text"     case suspendedBg = "suspendedBg"     case textSelectionBg = "text-selection-bg"     case timezoneColor = "timezone-color"     case verticalSliderLines = "vertical-slider-lines"     case textColor = "textColor"     case timeframeSelected = "timeframeSelected"     case aggregationSelectedCellText = "aggregationSelectedCellText"     case indicatorValueBg = "indicatorValueBg"     case buttonBorder = "button-border" } `} --- ## Chart for iOS/Configuration/Customization/Localization.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; ## Localization You can provide your own names for components. Just provide bundle for your `Localizable.strings` : {` // Example: configure custom source DXChartApp.resourceManager = DXCResourceManager(localizationBundle: .main) `} The list of used key/value properties is: {` //**MARK: Settings** "Setting.title" = "Settings"; "Setting.chartType" = "Chart type"; "Setting.highAndLow" = "High and Low"; "Setting.verticalGrid" = "Vertical grid"; "Setting.horizontalGrid" = "Horizontal grid"; "Setting.candleWicks" = "Candle wicks"; "Setting.watermark" = "Watermark"; "Setting.timezone" = "Timezone"; "Setting.theme" = "Theme"; "Setting.auto" = "Auto"; "Setting.dark" = "Dark"; "Setting.light" = "Light"; "Setting.bullishBody" = "Bullish body"; "Setting.bullishBorder" = "Bullish border"; "Setting.bearishBody" = "Bearish body"; "Setting.bearishBorder" = "Bearish border"; "Setting.up" = "Up"; "Setting.down" = "Down"; "Setting.none" = "None"; "Setting.area" = "Area"; "Setting.scatter" = "Scatter"; "Setting.doji" = "Doji"; "Setting.background" = "Background"; "Setting.watermarkColor" = "Watermark"; "Setting.grid" = "Grid"; "Setting.valuesOnScales" = "Values on scales"; "Setting.tradingFromChart" = "Trading from chart"; "Setting.showActiveOrders" = "Show active orders"; "Setting.showOpenPositions" = "Show open positions"; "Setting.autoscalePriceAxis" = "Auto-scale price axis"; "Setting.fitStudies" = "Fit studies"; "Setting.fitOrders" = "Fit orders"; "Setting.fitPositions" = "Fit positions"; "Setting.invertScale" = "Invert scale"; "Setting.lockScale" = "Lock scale"; "Setting.scaleType" = "Scale type"; "Setting.sessionBreaks" = "Session breaks"; "Setting.extendedHours" = "Extended hours"; "Setting.alignDataWithSessionStart" = "Align data with session start"; "Setting.priceType" = "Price type"; "Setting.volume" = "Volume"; "Setting.eventsOnChart" = "Events on chart"; "Setting.dividends" = "Dividends"; "Setting.splitsAndConsolidations" = "Splits and consolidations"; "Setting.earningsAndEstimates" = "Earnings and estimates"; "Setting.conferenceCalls" = "Conference calls"; "Setting.news" = "News"; "Setting.reset" = "Reset"; "Setting.on" = "On"; "Setting.off" = "Off"; //**MARK: Settings sections** "SettingsSection.General" = "General"; "SettingsSection.Colors" = "Colors"; "SettingsSection.Trading" = "Trading"; "SettingsSection.Scale" = "Scale"; "SettingsSection.Data" = "Data"; "SettingsSection.Events" = "Events"; //**MARK: Main Screen** "Main.remove" = "Remove"; "Main.done" = "Done"; "Main.clearAll" = "Clear"; "Main.suspendedTrading" = " Trading has been suspended"; "Main.buy" = "Buy %@"; "Main.sell" = "Sell %@"; "Main.change" = "%@ %%"; "Main.NoInternet.title" = "Chart Unavailable"; "Main.NoInternet.subtitle"= "No connection to the internet"; "Main.NoData.title" = "No data available"; "Main.NoData.subtitle"= "Please try again in a minute\n or try a different symbol"; //**MARK: Aggregation time** "Aggregation.custom" = "Create custom"; "Aggregation.second" = "second"; "Aggregation.seconds" = "seconds"; "Aggregation.minute" = "minute"; "Aggregation.minutes" = "minutes"; "Aggregation.hour" = "hour"; "Aggregation.hours" = "hours"; "Aggregation.day" = "day"; "Aggregation.days" = "days"; "Aggregation.week" = "week"; "Aggregation.weeks" = "weeks"; "Aggregation.year" = "year"; "Aggregation.years" = "years"; "Aggregation.month" = "month"; "Aggregation.add" = "Add"; //**MARK: Drawings** "Drawings.title" = "Drawings"; "Drawings.trendLine" = "Trend Line"; "Drawings.infoLine" = "Info Line"; "Drawings.horizontalRay" = "Horizontal Ray"; "Drawings.arrow" = "Arrow"; "Drawings.ray" = "Ray"; "Drawings.extendedLine" = "Extended Line"; "Drawings.priceLine" = "Price Line"; "Drawings.timeLine" = "Time Line"; "Drawings.trendChannel" = "Trend Channel"; "Drawings.regressionTrend" = "Regression Trend"; "Drawings.pitchfork" = "Pitchfork"; "Drawings.fibonacciRays" = "Fibonacci Rays"; "Drawings.fibonacciArcs" = "Fibonacci Arcs"; "Drawings.fibRetracements" = "Fibonacci Retracements"; "Drawings.fibExtension" = "Fibonacci Extension"; "Drawings.fibChannel" = "Fibonacci Channel"; "Drawings.fibSpiral" = "Fibonacci Spiral"; "Drawings.fibCircles" = "Fibonacci Circles"; "Drawings.fibTimezone" = "Fibonacci Timezone"; "Drawings.gannSquare" = "Gann Square"; "Drawings.gannBox" = "Gann Box"; "Drawings.gannFan" = "Gann Fan"; "Drawings.highlighter" = "Highlighter"; "Drawings.brush" = "Brush"; "Drawings.path" = "Path"; "Drawings.curve" = "Curve"; "Drawings.arrowUp" = "Arrow Up"; "Drawings.arrowDown" = "Arrow Down"; "Drawings.oval" = "Oval"; "Drawings.rectangle" = "Rectangle"; "Drawings.elliottImpulse" = "Elliott Impulse (12345)"; "Drawings.elliottCorrection" = "Elliott Correction (ABC)"; "Drawings.priceRange" = "Price Range"; "Drawings.dateRange" = "Date Range"; "Drawings.dateAndPriceRange" = "Date and Price Range"; "Drawings.priceLabel" = "Price label"; "Drawings.callout" = "Callout"; "Drawings.icon" = "Icon"; "Drawings.text" = "Text"; "Drawings.fibTimeExtension" = "Fibonacci Time Extension"; "Drawings.cycleBrackets" = "Cycle Brackets "; "Drawings.fibTimeRatios" = "Fibonacci Time Ratios"; "Drawings.arc" = "Arc"; //**MARK: DrawingSections** "DrawingsSections.treendLines" = "Treend lines"; "DrawingsSections.fibonacciAndGanns" = "Fibonacci and ganns"; "DrawingsSections.markups" = "Markups"; "DrawingsSections.elliots" = "Elliots"; "DrawingsSections.ranges" = "Ranges"; "DrawingsSections.annotations" = "Annotations"; //**MARK: ChartType** "ChartType.candle" = "Candles"; "ChartType.bar" = "Bar"; "ChartType.line" = "Line"; "ChartType.area" = "Area"; "ChartType.equivolume" = "Equivolume"; "ChartType.heikinAshi" = "Heikin-Ashi"; "ChartType.scatter" = "Scatter"; "ChartType.hollowCandle" = "Hollow Candle"; "ChartType.histogram" = "Histogram"; "ChartType.baseline" = "Baseline"; "ChartType.trend" = "Trend"; //**MARK: PriceType** "PriceType.last" = "Last"; "PriceType.market" = "Market"; "PriceType.bid" = "Bid"; "PriceType.ask" = "Ask"; //**MARK: ScaleType** "ScaleType.regular" = "Regular"; "ScaleType.percent" = "Percent"; "ScaleType.logarithmic" = "Logarithmic"; //**MARK: MagnetMode** "MagnetMode.title" = "Magnet mode"; "MagnetMode.description" = "The magnet will help you snap drawings to the data on the chart. Tap the magnet icon again to disable it."; //**MARK: DrawingMode** "DrawingMode.title" = "Drawing mode"; "DrawingMode.description" = "In this mode you will continue using the drawing you choose until you turn drawing mode off or save work."; //**MARK: TextDrawingHelp** "TextDrawingHelp.title" = "Double tap to edit"; "TextDrawingHelp.description" = "All text drawings are easy to\n change by double tapping on\n a bubble with a text."; //**MARK: Timezone** "Timezone.exchange" = "Exchange"; "Timezone.title" = "Timezone"; //**MARK: EditText** "EditText.placeholder" = "Write annotation..."; "EditText.screenTitle" = "Add text"; //**MARK: Search** "Search.symbol.title" = "Symbol not found"; "Search.symbol.subtitle" = "Your search did not match \nany symbol or description"; "Search.timezone.title" = "Timezone not found"; "Search.timezone.subtitle" = "Your search did not match \nany timezone"; "Search.indicator.title" = "Indicator not found"; "Search.indicator.subtitle" = "Your search did not match \nany indicator"; //**MARK: Template** "Template.delete" = "Delete template"; "Template.newTemplateWithNumb" = "New Template %d"; "Template.newTemplate" = "New Template"; "Template.EditTemplate" = "Edit Template"; "Template.CreateNewTemplate" = "Create new template"; "Template.CreateTemplate" = "Create template"; //**MARK: Indicator Screen** "Indicator.title" = "Indicators"; "Indicator.turnOff" = "Turn off all"; "Indicator.configure" = "Configure"; //**MARK: Events** "Events.Splits" = "Split %d/%d"; "Events.Date" = "Date"; "Events.Gross" = "Gross"; "Events.BasicEPS" = "Basic EPS"; "Events.DilutedEPS" = "Diluted EPS"; "Events.PeriodEnding" = "Period Ending"; //**MARK: Alert** "Alert.SessionBreaks.title" = "Session breaks are disabled"; "Alert.SessionBreaks.body" = "You can't change session breaks while aggregation is more then 1 day"; "Alert.ExtendedHours.title" = "Extended hours are disabled"; "Alert.ExtendedHours.body" = "Cannot display data beyond one day. To enable extended hours, adjust timeframe on home screen."; "Alert.ScaleTypePercent.title" = "Setting is disabled"; "Alert.ScaleTypePercent.body" = "You can't change this setting while scale type is percent"; "Alert.ScaleTypeNotRegular.title" = "Setting is disabled"; "Alert.ScaleTypeNotRegular.body" = "You can't change this setting while scale type is not regular"; "Alert.Aggregation.NotSupportedSecondsAggregations.title" = "Real-time data is not supported for \"seconds\" aggregation"; `} --- ## Chart for iOS/Configuration/Data Providers/Candles Provider.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Candles Provider Provider that supplies data about candles for the selected trading instrument. The chart listens to `candlesPublisher`, a flow of candle data updates, and reacts to changes in it by updating the displayed information accordingly. Whenever there is a new candle data emitted by `candlesPublisher`, the chart updates to reflect this new data, ensuring that the visual representation of the trading instrument's price movements is always current and accurate. {` import Combine /// Protocol for receiving candle data and managing the state of the connection. /// /// Provider that supplies data about candles for the selected trading instrument. The chart listens to \`candlesPublisher\`, /// a flow of candle data updates, and reacts to changes in it by updating the displayed information accordingly. /// Whenever there is a new candle data emitted by \`candlesPublisher\`, the chart updates to reflect this new data, /// ensuring that the visual representation of the trading instrument's price movements is always current and accurate. public protocol CandlesProvider { /// Publisher for receiving an array of \`Candle\` data. /// /// Provides the publisher of candle data for the requested symbol. var candlesPublisher: AnyPublisher<[Candle], Never> { get } /// Publisher for receiving the availability state of candle data. /// /// Emits \`true\` when data is available, \`false\` otherwise. var isDataAvailable: AnyPublisher { get } /// Publisher for receiving the loading state of candle data after parameters change. /// /// Emits \`true\` when candle data is being loaded, \`false\` when loading is complete. var isLoading: AnyPublisher { get } /// Publisher for receiving the current connection state. /// /// Tracks the state of the connection to the candle data provider. var connectionState: AnyPublisher { get } /// Updates the parameters for receiving candle data. /// /// - Parameters: /// - symbol: The instrument symbol for which the candle data is requested. /// - priceType: The type of price to use for the instrument (bid, ask, last, market). /// - aggregation: The aggregation settings for the candle data (time interval or granularity). /// - extendedHours: Flag indicating whether to include extended trading hours in the data. /// - alignSessionStart: Flag indicating whether to align data to the start of the trading session. func changeParams( symbol: String, priceType: PriceType, aggregation: Aggregation, extendedHours: Bool, alignSessionStart: Bool ) } `} ### Candle data {` /// Represents a financial market candle (OHLCV data) for a specific time period. /// /// The \`Candle\` structure holds data related to a specific time period, commonly used in financial charts to represent price movement. /// It includes open, high, low, and close prices, along with volume, timestamp, and optional VWAP (volume-weighted average price). /// /// - Commonly used in candlestick charts to show price movement within a given time interval. public struct Candle: Codable, Hashable { /// The closing price of the asset for the specific time period. public var close: Double /// The highest price reached during the specific time period. public var hi: Double /// The lowest price reached during the specific time period. public var lo: Double /// The opening price of the asset for the specific time period. public var open: Double /// The timestamp (in milliseconds since epoch) representing the start of the time period. /// /// This timestamp is often used to place the candle in the correct position in a time-based chart. public var timestamp: Int64 /// The trading volume (number of units traded) during the specific time period. public var volume: Double /// The volume-weighted average price (VWAP) for the specific time period. /// /// VWAP is an optional value that represents the average price weighted by volume. It helps indicate the true average price over the time period. public var vwap: Double? } `} ## Default implementation If you haven't implemented a Candles data provider earlier and are using [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api) to retrieve data, you can use our default provider. To work with it and DXFeedFramework, you need a Quote Address to obtain candles and quotes data. Please contact your DXFeed sales manager or visit https://dxfeed.com/contact-sales/ for assistance. Default implementation code: {` import Combine import DXChart import DXFeedFramework final class DXFeedCandlesProvider { private var _candlesPublisher = PassthroughSubject<[DXChart.Candle], Never>() private var _isDataAvailable = PassthroughSubject() private var _isLoading = PassthroughSubject() private var _connectionState = PassthroughSubject() private let address: String private var symbol: String = "" private var priceType: DXChart.PriceType private var aggregation: DXChart.Aggregation private var extendedHours: Bool private var alignDataWithSessionStart: Bool /// Start Date store value in milliseconds from \`Wed, 31 Dec 2014 20:00:00 GMT\` private let startDate: Int64 = Date(timeIntervalSince1970: 1420056000).millisecondsSince1970 private var timeSymbol: TimeSeriesSubscriptionSymbol { let aggregation = aggregation let priceType = priceType == .last ? "," : ",price=\(priceType.asString)," let symbolStr = "\(symbol){=\(aggregation.forConnection)\(priceType)tho=\(!extendedHours),\(alignDataWithSessionStart ? "a=s" : "")}" let timeSeriesSubscriptionSymbol = TimeSeriesSubscriptionSymbol(symbol: symbolStr, fromTime: startDate) return timeSeriesSubscriptionSymbol } private var debouncingTimer: Timer? = nil private let debouncingTime: TimeInterval = 12 private var receivedEvent = false private var candleSubscription: DXFeedSubscription? private var endpoint: DXEndpoint? private let snapshotProcessor = SnapshotProcessor() init(address: String, symbol: String, priceType: DXChart.PriceType, aggregation: DXChart.Aggregation, extendedHours: Bool, alignDataWithSessionStart: Bool) { self.address = address self.symbol = symbol self.priceType = priceType self.aggregation = aggregation self.extendedHours = extendedHours self.alignDataWithSessionStart = alignDataWithSessionStart snapshotProcessor.add(self) connect() } convenience init(address: String, dataStorage: DXChart.DataStorage, settingsManager: DXChart.SettingsManager) { self.init( address: address, symbol: dataStorage.instrument.symbol, priceType: settingsManager.priceType, aggregation: dataStorage.aggregation, extendedHours: settingsManager.extendedHours, alignDataWithSessionStart: settingsManager.alignDataWithSessionStart ) } } extension DXFeedCandlesProvider: DXChart.CandlesProvider { var candlesPublisher: AnyPublisher<[DXChart.Candle], Never> { _candlesPublisher.eraseToAnyPublisher() } var isDataAvailable: AnyPublisher { _isDataAvailable.eraseToAnyPublisher() } var isLoading: AnyPublisher { _isLoading.eraseToAnyPublisher() } var connectionState: AnyPublisher { _connectionState.eraseToAnyPublisher() } func changeParams(symbol: String, priceType: DXChart.PriceType, aggregation: DXChart.Aggregation, extendedHours: Bool, alignSessionStart: Bool) { self.symbol = symbol self.priceType = priceType self.aggregation = aggregation self.extendedHours = extendedHours self.alignDataWithSessionStart = alignSessionStart reconnect() } } private extension DXFeedCandlesProvider { func connect() { receivedEvent = false candleSubscription = nil _isLoading.send(true) try? SystemProperty.setProperty(DXEndpoint.ExtraProperty.heartBeatTimeout.rawValue, "10s") if endpoint == nil { endpoint = try? DXEndpoint.builder().withRole(.feed).build() endpoint?.add(listener: self) } _ = try? endpoint?.connect(address) candleSubscription = try? endpoint?.getFeed()?.createSubscription(Candle.self) try? candleSubscription?.add(listener: snapshotProcessor) try? candleSubscription?.addSymbols(timeSymbol) checkData() } func disconnect() { candleSubscription?.remove(listener: snapshotProcessor) candleSubscription = nil } func reconnect() { disconnect() connect() } func checkData() { DispatchQueue.main.async { guard self.debouncingTimer == nil || self.debouncingTimer?.isValid == false else { return } self.debouncingTimer = Timer.scheduledTimer( withTimeInterval: self.debouncingTime, repeats: false) { [weak self] timer in timer.invalidate() guard let self else { return } self.debouncingTimer = nil guard !self.receivedEvent else { return } self._isDataAvailable.send(false) self._isLoading.send(false) } } } } extension DXFeedCandlesProvider: Hashable { static func == (lhs: DXFeedCandlesProvider, rhs: DXFeedCandlesProvider) -> Bool { return lhs === rhs || lhs.address == rhs.address } func hash(into hasher: inout Hasher) { hasher.combine(address) } } extension DXFeedCandlesProvider: SnapshotDelegate { func receiveEvents(_ events: [DXFeedFramework.MarketEvent], isSnapshot: Bool) { receivedEvent = true _candlesPublisher.send(events.convertToCandles()) _isDataAvailable.send(true) _isLoading.send(false) debouncingTimer?.invalidate() debouncingTimer = nil } } private extension Array where Element == DXFeedFramework.MarketEvent { func convertToCandles() -> [DXChart.Candle] { guard let dxCandlesArray = self as? [DXFeedFramework.Candle] else { return [] } let candlesArray: [DXChart.Candle] = dxCandlesArray.compactMap { event in let candle = event.candle return DXChart.Candle( close: candle.close, hi: candle.high, lo: candle.low, open: candle.open, timestamp: candle.time, volume: candle.volume.isNaN ? 0 : candle.volume, vwap: candle.vwap.isNaN ? nil : candle.vwap ) } return candlesArray.reversed() } } extension DXFeedCandlesProvider: DXEndpointListener { func endpointDidChangeState(old: DXFeedFramework.DXEndpointState, new: DXFeedFramework.DXEndpointState) { let result: ConnectionState = switch (new) { case .notConnected: .notConnected case .connecting: .connecting case .connected: .connected case .closed: .closed @unknown default: .notConnected } _connectionState.send(result) } } // String representation for DXFeed connection extension Aggregation { var forConnection: String { switch timeUnit { case .second: "\(value)s" case .minute: "\(value)m" case .hour: "\(value)h" case .day: "\(value)d" case .week: "\(value)w" case .month: "\(value)mo" case .year: "\(value)y" } } } extension DXChart.PriceType { var asString: String { switch self { case .market: "mark" case .bid: "bid" case .ask: "ask" case .last: "last" } } } `} --- ## Chart for iOS/Configuration/Data Providers/Data Provider.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Data Provider You should implement a data provider that conforms to the `DataProvider` protocol to provide the necessary dependencies for `ChartScreen`. DataProvider **protocol** acts **as** a central place to access different data providers such **as** candles, quotes, IPF, schedule, and news. Types conforming to `DataProvider` must provide access to a variety of services via properties, with **some** being **optional** depending on the implementation. {` /// A protocol that defines the necessary data providers for a system. /// /// This protocol acts as a central place to access different data providers /// such as candles, quotes, IPF, schedule, and news. /// /// Types conforming to \`DataProvider\` must provide access to a variety of services /// via properties, with some being optional depending on the implementation. public protocol DataProvider { /// Provides access to the candle data for a specific instrument. /// Used for fetching and streaming historical and real-time candle data. var candlesProvider: CandlesProvider { get } /// Provides access to the quotes data for a specific instrument. /// Facilitates fetching bid, ask, and last price data for the selected symbol. var quotesDataProvider: QuotesDataProvider { get } /// Provides access to the IPF (Instrument Price Flow) data. /// Used for tracking and visualizing instrument price flow information. var ipfDataProvider: IPFDataProvider { get } /// Provides access to the instrument's trading schedule. /// Useful for aligning data with trading session start times and identifying trading windows. var scheduleProvider: ScheduleProvider { get } /// Provides access to news data for the selected symbol, including corporate actions, earnings, and conference calls. /// May be \`nil\` if news functionality is not supported or not required. var newsProvider: NewsProvider? { get } } `} ## Default implementation If you haven’t implemented data providers before and are using [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api) to retrieve data, you can use our default provider. To work with it and DXFeedFramework, you will need a Quote Address to obtain candles and quotes data, and an IPF Address to obtain instrument information. Additionally, a default implementation of the Studies Provider is available if you are using the DXStudies framework. For assistance, please contact your DXFeed or DXStudies sales manager or visit https://dxfeed.com/contact-sales/ for assistance. Default implementation code: {` import DXChart import DXFeedFramework final class DataProvider: DXChart.DataProvider { var candlesProvider: DXChart.CandlesProvider var quotesDataProvider: DXChart.QuotesDataProvider var ipfDataProvider: DXChart.IPFDataProvider var scheduleProvider: DXChart.ScheduleProvider var newsProvider: DXChart.NewsProvider? init( quoteAddress: String, ipfAddress: String, dataStorage: DXChart.DataStorage = DXChartApp.dataStorage, settingsManager: DXChart.SettingsManager = DXChartApp.settingsManager ) { let currentSymbol = dataStorage.instrument.symbol candlesProvider = DXFeedCandlesProvider(address: quoteAddress, dataStorage: dataStorage, settingsManager: settingsManager) quotesDataProvider = DXFeedQuotesDataProvider(address: quoteAddress, symbol: currentSymbol) ipfDataProvider = DXFeedIPFDataProvider(ipfAddress) scheduleProvider = DXFeedScheduleProvider() newsProvider = DXFeedNewsProvider(currentSymbol: currentSymbol) } } `} --- ## Chart for iOS/Configuration/Data Providers/IPF Data Provider.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # IPF Data Provider Provider supplying a list of data about trading instruments that can be set on the chart, along with their trading hours. {` import Combine /// Protocol for receiving instrument profile data - array of InstrumentData. /// /// When loading DXChart framework, an array of instruments, their descriptions, etc. are taken from this interface /// /// When connecting DXChart framework, developer can implement this interface or use the default implementation [link on the default implementation] and pass it to the framework using DXChart.DataProvider class public protocol IPFDataProvider { /// Publisher of receiving instrument profile data. /// /// Instrument profile data is represented by array of InstrumentData. var dataPublisher: AnyPublisher, Never> { get } /// Provide information about loading status. var isLoading: AnyPublisher { get } } `} Possible Server Errors: {` public enum ServerError: Error { case unknown case noData case parseError case urlError case tooManyRequests case unknown } `} InstrumentData structure uses following parameters: {` /// A structure that represents a financial instrument. /// /// The \`Instrument\` struct stores essential information about a financial instrument, such as its type, symbol, description, time zone, price increments, and trading schedule. /// /// - Note: This struct conforms to \`Codable\`, allowing it to be easily encoded and decoded for serialization purposes. /// /// - Properties: /// - \`type\`: The type of the instrument (e.g., \`STOCK\`, \`CRYPTO\`). /// - \`symbol\`: The symbol of the instrument (e.g., \`GOOG\`, \`TSLA\`, \`AAPL\`). /// - \`description\`: A description of the instrument (e.g., \`Alphabet Inc. - Class C Capital Stock\`). /// - \`timezone\`: The time zone associated with the instrument (e.g., \`America/New_York\`). /// - \`priceIncrements\`: The minimum price increment for the instrument (e.g., \`0.01\`). /// - \`schedule\`: The trading schedule for the instrument, including session breaks and extended hours. This property is optional. public struct Instrument: Codable { /// The type of the instrument (e.g., \`STOCK\`, \`CRYPTO\`). public var type: String /// The symbol of the instrument (e.g., \`GOOG\`, \`TSLA\`, \`AAPL\`). public var symbol: String /// A description of the instrument (e.g., \`Alphabet Inc. - Class C Capital Stock\`). public var description: String /// The time zone associated with the instrument (e.g., \`America/New_York\`). public var timezone: String /// The minimum price increment for the instrument (e.g., \`0.01\`). public var priceIncrements: String /// The trading schedule for the instrument, including session breaks and extended hours. /// This property is optional and can be \`nil\` if no schedule is available. public var schedule: Trading.Schedule? } @available(*, deprecated, renamed: "Instrument", message: "This typealias is deprecated and will be removed in a future version. Use \`Instrument\` directly instead.") /// A deprecated typealias for \`Instrument\`. /// /// This typealias is provided for backward compatibility and will be removed in a future version. /// Use \`Instrument\` directly instead. public typealias InstrumentData = Instrument `} - type - Type of instrument (f.e. STOCK, CRYPTO) - symbol - Symbol of the instrument (e.g., "GOOG", "TSLA", "AAPL"). - description - Description of the instrument (e.g., "Alphabet Inc. - Class C Capital Stock"). - timezone - Raw data about timezone of the instrument (e.g., "Asia/Istanbul", "America/New_York"). - sessionBreaks - Parsed trading hours of the instrument. (e.g., "BIST(name=BIST;tz=Asia/Istanbul;hd=TR;sd=TR;td=12345;de=+0000;0=10001800)"). - priceIncrements - Minimal price increments of the instrument (e.g., "0.01 20; 0.02 50; 0.05 100; 0.1 250; 0.25 500; 0.5 1000; 1 2500; 2.5"). Received data about instruments is displayed in the list of instruments: ![image-2024-1-29_0-40-22](../../../images/instruments.png) ## Default implementation If you haven't implemented an IPF data provider earlier and are using [\`DXFeedFramework\`](https://github.com/dxFeed/dxfeed-graal-swift-api) to retrieve data, you can use our default provider. To work with it and [\`DXFeedFramework\`](https://github.com/dxFeed/dxfeed-graal-swift-api), you need an \`IPF Address\` to obtain instrument information. Please contact your DXFeed sales manager or visit https://dxfeed.com/contact-sales/ for assistance. Default implementation code: {` import Combine import DXChart import DXFeedFramework final class DXFeedIPFDataProvider: DXChart.IPFDataProvider { typealias IPFResult = Result<[DXChart.Instrument], Error> let address: String private let dataStorage = DataStorage.shared private var instruments: [DXChart.Instrument]? private var _isLoading = PassthroughSubject() var isLoading: AnyPublisher { _isLoading.eraseToAnyPublisher() } lazy var dataPublisher: AnyPublisher = Deferred { [weak self] in Future { promise in guard let self else { return promise(.success(.failure(ServerError.noData)))} if let instruments = self.instruments { return promise(.success(.success(instruments))) } DispatchQueue.global(qos: .userInitiated).async { self._isLoading.send(true) defer { self._isLoading.send(false) } let reader = DXInstrumentProfileReader() do { let result = try reader.readFromFile(address: self.address) guard let result else { return promise(.success(.failure(ServerError.noData))) } let instrumentsData = result.map { ipf in let timeZone = ipf.tradingHours.sliceMultipleTimes(from: "tz=", to: ";") let data = Instrument( type: ipf.type, symbol: ipf.symbol, description: ipf.descriptionStr, timezone: timeZone.first ?? "America/New_York", priceIncrements: ipf.priceIncrements ) self.dataStorage.setTradingHours(symbol: ipf.symbol, tradingHours: ipf.tradingHours) return data } self.instruments = instrumentsData return promise(.success(.success(instrumentsData))) } catch { return promise(.success(.failure(ServerError.unknown))) } } } }.eraseToAnyPublisher() init(_ address: String) { self.address = address } } fileprivate extension String { func sliceMultipleTimes(from: String, to: String) -> [String] { components(separatedBy: from).dropFirst().compactMap { sub in (sub.range(of: to)?.lowerBound).flatMap { endRange in String(sub[sub.startIndex ..< endRange]) } } } } `} --- ## Chart for iOS/Configuration/Data Providers/News Provider.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # News Provider Provider that supplies data about events for the selected trading instrument (corporate actions, corporate calendar, earning reports) and news. {` import Combine /// A protocol for providing news data related to a specific financial instrument. /// /// The \`NewsProvider\` protocol defines the required functionalities for any provider that supplies /// news data, corporate actions, conference call information, and earnings data for a presented symbol. /// Conforming types must implement the necessary publishers to push relevant data updates. /// /// This protocol is useful for applications that need to display timely news and corporate information /// to users based on the selected financial instrument. public protocol NewsProvider { /// A publisher that emits an array of \`NewsItem\` instances related to the presented symbol. /// /// The publisher sends updates as a result of fetching news data, providing either /// a success case containing an array of news items or an error case if the request fails. var newsPublisher: AnyPublisher, Never> { get } /// A publisher that emits an array of \`CorporateAction\` instances. /// /// This publisher sends updates on corporate actions (e.g., stock splits, dividends) relevant to /// the presented symbol, similar to the \`newsPublisher\` in structure and functionality. var corporateActionPublisher: AnyPublisher, Never> { get } /// A publisher that emits an array of \`Earning\` instances related to conference calls. /// /// This publisher provides information about upcoming and past conference calls for the /// presented symbol, allowing consumers to stay informed about earnings announcements. var conferenceCallPublisher: AnyPublisher, Never> { get } /// A publisher that emits an array of \`Earning\` instances related to earnings reports. /// /// This publisher sends updates on earnings data for the symbol, helping consumers track performance /// and financial metrics associated with the instrument. var earningsPublisher: AnyPublisher, Never> { get } /// Updates the data for the specified financial instrument symbol. /// /// - Parameter symbol: The symbol for which to fetch and update news data. /// This method should be called whenever the user selects a different symbol to display /// its associated news and corporate actions. func updateData(for symbol: String) } `} Events set on the chart look as follows: ![events_on_chart](../../../images/events_on_chart.png) ## Default example If you haven't implemented a provider for News, Earnings, Corporate Actions, and Conference Calls earlier and are using [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api) to retrieve data, you can use our default provider. To work with it and DXFeedFramework, you need credentials such as a username and password, which should be placed in the placeholders within the Headers section of the Network Provider. Please contact your DXFeed sales manager or visit https://dxfeed.com/contact-sales/ for assistance. Default implementation code: {` import Combine import DXChart final class DXFeedNewsProvider: DXChart.NewsProvider { private let _newsPublisher = PassthroughSubject, Never>() private let _corporateActionPublisher = PassthroughSubject, Never>() private let _conferenceCallPublisher = PassthroughSubject, Never>() private let _earningsPublisher = PassthroughSubject, Never>() private let networkManager = NetworkManager() var newsPublisher: AnyPublisher, Never> { _newsPublisher.eraseToAnyPublisher() } var corporateActionPublisher: AnyPublisher, Never> { _corporateActionPublisher.eraseToAnyPublisher() } var conferenceCallPublisher: AnyPublisher, Never> { _conferenceCallPublisher.eraseToAnyPublisher() } var earningsPublisher: AnyPublisher, Never> { _earningsPublisher.eraseToAnyPublisher() } init(currentSymbol: String) { updateData(for: currentSymbol) } func updateData(for symbol: String) { sendNewsListRequest() getCorporateAction() getConferenceCall() getEarnings() } private func sendNewsListRequest() { networkManager.sendNewsListRequest { [weak self] result in guard let self else { return } switch result { case .success(let news): self._newsPublisher.send(.success(news)) case .failure(let error): switch error { case ServerError.tooManyRequests: DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in self?.sendNewsListRequest() }) default: self._newsPublisher.send(.failure(error)) } } } } func getCorporateAction() { let completion: (Result<[DXChart.CorporateAction], Error>) -> Void = { [weak self] result in guard let self else { self?._corporateActionPublisher.send(Result.failure(ServerError.noData)) return } switch result { case .success(let response): _corporateActionPublisher.send(.success(response)) case .failure(let error): switch error { case ServerError.tooManyRequests: DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in self?.getConferenceCall() }) default: _corporateActionPublisher.send(.failure(error)) } } } networkManager.sendCorporateActionRequest(completion: completion) } private func getConferenceCall() { let completion: (Result<[DXChart.Earning], Error>) -> Void = { [weak self] result in guard let self else { self?._conferenceCallPublisher.send(Result.failure(ServerError.noData)) return } switch result { case .success(let response): _conferenceCallPublisher.send(.success(response)) case .failure(let error): switch error { case ServerError.tooManyRequests: DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in self?.getConferenceCall() }) default: _conferenceCallPublisher.send(.failure(error)) } } } networkManager.sendConferenceCallRequest(completion: completion) } private func getEarnings() { let completion: (Result<[DXChart.Earning], Error>) -> Void = { [weak self] result in guard let self else { self?._earningsPublisher.send(Result.failure(ServerError.noData)) return } switch result { case .success(let response): _earningsPublisher.send(.success(response)) case .failure(let error): switch error { case ServerError.tooManyRequests: DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in self?.getEarnings() }) default: _earningsPublisher.send(.failure(error)) } } } networkManager.sendEarningsRequest(completion: completion) } } `} ### This example include next data types: #### Network Manager {` import DXChart // Models typealias ServerError = DXChart.ServerError struct NewsResponse: Codable { let news: [DXChart.NewsItem] } // Network manager final class NetworkManager { private let decoder = JSONDecoder() func sendNewsListRequest(completion: @escaping (Result<[DXChart.NewsItem], Error>) -> Void) { sendRequest(request: .getNewsList, parse: parseNewsList) { response in switch response { case .success(let newsResponse): completion(.success(newsResponse.news)) case .failure(let error): completion(.failure(error)) } } } func sendCorporateActionRequest(completion: @escaping (Result<[DXChart.CorporateAction], Error>) -> Void) { sendRequest(request: .getCorporateAction, parse: parseCorporateAction, completion: completion) } func sendConferenceCallRequest(completion: @escaping (Result<[DXChart.Earning], Error>) -> Void) { sendRequest(request: .getCorporateCalendar, parse: parseConferenceCall, completion: completion) } func sendEarningsRequest(completion: @escaping (Result<[DXChart.Earning], Error>) -> Void) { sendRequest(request: .getEarningReport, parse: parseEarning, completion: completion) } func sendRequest(request: NetworkRequest, parse: @escaping (Data) -> T?, completion: @escaping (Result) -> Void) { do { let request = try request.makeURLRequest() let session = URLSession.shared let task = session.dataTask(with: request) { data, response, error in if let error { debugPrint("Error: \(error)") completion(.failure(ServerError.unknown)) return } guard let httpResponse = response as? HTTPURLResponse else { completion(.failure(ServerError.unknown)) return } switch httpResponse.statusCode { case 200...299: guard let data = data else { completion(.failure(ServerError.noData)) return } guard let parsedData = parse(data) else { completion(.failure(ServerError.parseError)) return } completion(.success(parsedData)) case 429: completion(.failure(ServerError.tooManyRequests)) case 401: completion(.failure(ServerError.notAuthorized)) default: completion(.failure(ServerError.unknown)) } } task.resume() } catch { debugPrint("Error creating request: \(error)") completion(.failure(ServerError.unknown)) } } private func parseNewsList(data: Data) -> NewsResponse? { do { let newsResponse = try decoder.decode(NewsResponse.self, from: data) return newsResponse } catch { debugPrint("Error decoding JSON news: \(error)") return nil } } private func parseCorporateAction(data: Data) -> [CorporateAction]? { do { let corporateAction = try decoder.decode([CorporateAction].self, from: data) return corporateAction } catch { debugPrint("Error decoding JSON CorporateAction: \(error)") return nil } } private func parseConferenceCall(data: Data) -> [Earning]? { do { let conferenceCalls = try decoder.decode([Earning].self, from: data) let calls = conferenceCalls.filter { DXChart.AnnounceType(rawValue: $0.announceType) == .conferenceCall } return calls } catch { debugPrint("Error decoding JSON ConferenceCall: \(error)") return nil } } private func parseEarning(data: Data) -> [Earning]? { do { let earnings = try decoder.decode([Earning].self, from: data) let earningsReport = earnings.filter { DXChart.AnnounceType(rawValue: $0.announceType) == .earning } return earningsReport } catch { debugPrint("Error decoding JSON Earning: \(error)") return nil } } } `} #### NetworkRequest {` import DXChart enum NetworkRequest { case getNewsList case getCorporateAction case getCorporateCalendar case getEarningReport } extension NetworkRequest: DXChart.NetworkRequest { public var baseURL: String { switch self { case .getNewsList: "https://demo.dxfeed.com/" case .getCorporateAction, .getCorporateCalendar, .getEarningReport: "https://tools.dxfeed.com/" } } var path: String { let instrument = DXCUserDefaultsManager.shared.instrument return switch self { case .getNewsList: "news-demo?symbol=\(instrument.symbol)" case .getCorporateAction: "fs/v0.1/corporate-action/symbol/\(instrument.symbol)" case .getEarningReport, .getCorporateCalendar: "fs/v0.1/earning/symbol/\(instrument.symbol)" } } var method: String { "GET" } var params: [String: Any]? { nil } var headers: [String: String]? { switch self { case .getNewsList: let credentials = "<#provided login#>:<#provided password#>" let encodedCredentials = Data(credentials.utf8).base64EncodedString() return ["Authorization": "Basic \(encodedCredentials)"] case .getCorporateAction, .getCorporateCalendar, .getEarningReport: let credentials = "<#provided login#>:<#provided password#>" let encodedCredentials = Data(credentials.utf8).base64EncodedString() return ["Authorization": "Basic \(encodedCredentials)"] } } } `} ### NewsItem {` /// A struct representing a news item that can be displayed in a chart or other UI component. /// /// This model contains key information about a news event, including its title, publication time, and a URL to the full content. /// It is designed to be used in conjunction with components that display financial data, like charts, where the title and /// timestamp of the news are shown and, optionally, a clickable link to the full content is provided. public struct NewsItem: Codable { /// The URL to the full news content. /// /// If "body" is "nil", the news item will not be clickable, meaning that the item serves only as an informational piece. /// If "body" is not "nil", the news item can be tapped or clicked on to open the associated URL in a web browser, allowing /// users to view the full news article or content. /// /// - Example: "https://www.example.com/news/12345" public let body: URL? /// The timestamp of when the news item was published, in milliseconds since the Unix epoch (1970-01-01T00:00:00Z). /// /// This value is useful for sorting or displaying the news items in chronological order. /// /// - Example: "1635964800000" (represents a time in November 2021) public let time: Int64? /// The title of the news item that will be displayed in the chart or list. /// /// This string provides a brief summary or headline of the news event, which is meant to catch the user's attention. /// It typically includes the company name, ticker symbol, and a short description of the news event. /// /// - Example: '"Apple Inc. (AAPL) Q4 2021 Earnings Call Transcript"' public let title: String /// Initializes a "NewsItem" instance. /// /// - Parameters: /// - body: A URL to the full news content. If "nil", the news will not be clickable. /// - time: The timestamp of the news in milliseconds since the Unix epoch. /// - title: The headline or title of the news. init(body: URL?, time: Int64?, title: String) { self.body = body self.time = time self.title = title } `} ### Corporate Action {` /// A struct representing a corporate action event such as earnings, dividends, stock splits, or conference calls. /// /// This structure contains all relevant data about a corporate action event, including the type of action, the event time, /// any adjustment value (e.g., for dividends), and optional additional information such as a split (via "Ext") and the event point on a chart. public struct CorporateAction: EventsData { /// The timestamp of the corporate action event in milliseconds since the Unix epoch (1970-01-01T00:00:00Z). public let eventTime: Int64 /// The type of the corporate action event. /// /// Examples include "EARNING", "DIVIDEND", "CONFERENCE_CALL", "STOCK_SPLIT". public let announceType: String /// The date of the corporate action event in "YYYYMMDD" format. (example: 20150205) /// /// This integer represents the ex-date of the action, which is typically important for dividend and stock split events. public let ymd: Int /// The gross value associated with the corporate action event, typically for dividends. /// /// This is an optional value used mainly in the context of dividend events to represent the dividend value. public let adjustmentValue: Float? /// Additional information about the corporate action, particularly for stock splits. /// /// This optional field holds data related to stock splits, detailing the stock's value before and after the split (via the "Ext" struct). public let ext: Ext? /// An optional value used to recognize the event's point location on a chart. /// /// This can be used to visually place the corporate action event at a specific point on a chart, indicating the time and value. public let point: EventPoint? } `} Corporate actions and other events should comply with the EventsData protocol: {` /// A protocol for data types that represent events, such as corporate actions. /// /// Types conforming to "EventsData" provide key information about an event, including its timestamp, type, and point on a chart. public protocol EventsData: Codable { /// The timestamp of the event in milliseconds since the Unix epoch (1970-01-01T00:00:00Z). var eventTime: Int64 { get } /// The announceType of the event (e.g., "EARNING", "DIVIDEND", "STOCK_SPLIT"). var announceType: String { get } /// The point location of the event on a chart, if applicable. var point: EventPoint? { get } /// The date of the event in "YYYYMMDD" format. var ymd: Int { get } } `} ### Corporate Calendar Use Earning structure now see it below. ### Earning Report {` /// A struct representing an earnings event data point, containing key information about an earnings announcement. /// /// The "Earning" structure holds details such as the name of the earnings report, the event's date and time, the type of earnings announcement, /// as well as optional data like earnings per share (EPS) and estimated EPS values. public struct Earning: EventsData, Codable { /// The name of the earnings announcement. /// /// This is a string that describes the earnings event, such as the company name and the relevant quarter. public let name: String /// The date of the earnings event in "YYYYMMDD" format. Example: 20150205 /// /// This integer represents the specific date of the earnings announcement. public let ymd: Int /// The timestamp of the earnings event in milliseconds since the Unix epoch (1970-01-01T00:00:00Z). /// /// This value represents the time the earnings event occurred or was announced. Example: 1643416910176 public let eventTime: Int64 /// An optional point that indicates the location of this earnings event on a chart. /// /// The "EventPoint" can be used to visually place the earnings event on a chart, typically representing both time and value dimensions. public let point: EventPoint? /// The type of the earnings announcement. /// /// This string typically describes the type of earnings event, such as "EARNING", "DIVIDEND", "CONFERENCE_CALL", "STOCK_SPLIT". public let announceType: String /// The reference period for the earnings report (e.g., "Q4 2021"). /// /// This string describes the fiscal period or quarter to which the earnings announcement applies. let referencePeriod: String /// The actual earnings per share (EPS) reported during the earnings event. /// /// This is an optional value representing the EPS as reported by the company. let eps: Double? /// The estimated earnings per share (EPS) before the actual earnings report. /// /// This is an optional value that represents the expected EPS, typically predicted by analysts before the actual earnings report is released. let estimatedEPS: Double? } `} --- ## Chart for iOS/Configuration/Data Providers/Quotes Data Provider.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Quote Provider Quote Provider - provides data with selected trading instrument's candles that needs to be displayed on the chart AND quotes for the selected instrument, including Bid and Ask prices. {` import Combine /// Interface for receiving quotes data used in the DXChart framework. /// /// When loading a chart in the DXChart framework, quotes data is sourced from this interface. /// /// - Use \`dataPublisher\` to supply quotes data. /// - Use \`changeSymbol\` to change the instrument symbol for which quotes are being provided. public protocol QuotesDataProvider { /// Publisher that provides quotes data. /// /// The quotes data is represented by the \`Quote\` structure. This publisher emits the latest \`Quote\` data, and a \`nil\` value if no data is available. var dataPublisher: AnyPublisher { get } /// Changes the instrument symbol for which quotes are being provided. /// /// - Parameter symbol: The instrument symbol (e.g., "AAPL", "TSLA") for which the quotes data should be fetched. func changeSymbol(_ symbol: String) } `} #### Candle model includes next parameters: {` /// Represents a financial market candle (OHLCV data) for a specific time period. /// /// The \`Candle\` structure holds data related to a specific time period, commonly used in financial charts to represent price movement. /// It includes open, high, low, and close prices, along with volume, timestamp, and optional VWAP (volume-weighted average price). /// /// - Commonly used in candlestick charts to show price movement within a given time interval. public struct Candle: Codable, Hashable { /// The closing price of the asset for the specific time period. public var close: Double /// The highest price reached during the specific time period. public var hi: Double /// The lowest price reached during the specific time period. public var lo: Double /// The opening price of the asset for the specific time period. public var open: Double /// The timestamp (in milliseconds since epoch) representing the start of the time period. /// /// This timestamp is often used to place the candle in the correct position in a time-based chart. public var timestamp: Int64 /// The trading volume (number of units traded) during the specific time period. public var volume: Double /// The volume-weighted average price (VWAP) for the specific time period. /// /// VWAP is an optional value that represents the average price weighted by volume. It helps indicate the true average price over the time period. public var vwap: Double? } /// ⚠️ Deprecated ⚠️ public typealias CandlesResponse = Candle `} #### Possible Connection States: {` /// Represents the connection state. /// /// The "ConnectionState" enum defines the various states that a data connection can be in. /// This is useful for monitoring and handling the lifecycle of data feeds, such as connecting to a remote service, /// handling connection issues, and managing disconnections. public enum ConnectionState { /// The connection is not yet established. /// /// This state indicates that no attempt to connect has been made, or that a previous connection was closed and has not yet attempted to reconnect. case notConnected /// The connection is currently being established. /// /// This state is used when a connection attempt is in progress, but has not yet been completed. case connecting /// The connection is successfully established. /// /// This state indicates that the connection to the data provider is active, and data can be received. case connected /// The connection has been closed. /// /// This state is used when the connection was explicitly or automatically terminated. case closed } `} #### QuoteResultDelegate {` public protocol QuoteResultDelegate: AnyObject { func updateConnectionState(_ state: ConnectionState) func update(ask: Double, bid: Double) } `} `func update(ask: Double, bid: Double)` - a function to update bid and ask values on trading buttons. ## Default provider If you haven't implemented a Quote data provider earlier and are using [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api) to retrieve data, you can use our default provider. To work with it and DXFeedFramework, you need a Quote Address to obtain candles and quotes data. Please contact your DXFeed sales manager or visit https://dxfeed.com/contact-sales/ for more information. Default implementation code: {` import Combine import DXChart import DXFeedFramework final class DXFeedQuotesDataProvider: DXChart.QuotesDataProvider { let address: String private var endpoint: DXEndpoint? private var quoteSubscription: DXFeedSubscription? private var symbol: String init(address: String, symbol: String) { self.address = address self.symbol = symbol } private let _dataPublisher = PassthroughSubject() lazy var dataPublisher: AnyPublisher = Deferred { [unowned self] in self.connect() return _dataPublisher }.eraseToAnyPublisher() func changeSymbol(_ symbol: String) { self.symbol = symbol reconnect() } private func connect() { if endpoint == nil { endpoint = try? DXEndpoint.builder().withRole(.feed) .build() endpoint?.add(listener: self) _ = try? endpoint?.connect(address) } quoteSubscription = try? endpoint?.getFeed()?.createSubscription(Quote.self) try? quoteSubscription?.add(listener: self) try? quoteSubscription?.addSymbols(symbol) } private func disconnect() { quoteSubscription?.remove(listener: self) quoteSubscription = nil } private func reconnect() { disconnect() connect() } } extension DXFeedQuotesDataProvider: Hashable { static func == (lhs: DXFeedQuotesDataProvider, rhs: DXFeedQuotesDataProvider) -> Bool { return lhs === rhs || lhs.address == rhs.address } public func hash(into hasher: inout Hasher) { hasher.combine(address) } } extension DXFeedQuotesDataProvider: DXEventListener { func receiveEvents(_ events: [DXFeedFramework.MarketEvent]) { let lastEvent = events.last guard lastEvent?.type == .quote, let quote = lastEvent?.quote else { return } _dataPublisher.send(.init(from: quote)) } } extension DXFeedQuotesDataProvider: DXEndpointListener { public func endpointDidChangeState(old: DXFeedFramework.DXEndpointState, new: DXFeedFramework.DXEndpointState) { // ignore this state } } extension DXChart.Quote { init(from qoute: DXFeedFramework.Quote) { self.init(bidPrice: qoute.bidPrice, askPrice: qoute.askPrice) } } `} --- ## Chart for iOS/Configuration/Data Providers/Schedule Provider.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Schedule Provider The **Schedule Provider** is responsible for providing trading **session breaks** and **extended hours** data for the chart. It ensures accurate visualization of trading sessions, including pre-market, regular trading, after-market, and non-trading periods. This is essential for financial applications that require precise representation of market hours. {` /// A protocol that provides session breaks and extended hours data for the chart. /// /// The \`ScheduleProvider\` is responsible for supplying trading session breaks and extended hours information, /// which are used to highlight specific time ranges on the chart (e.g., market open/close times, breaks, or after-hours trading). /// /// When integrating with \`DXFeedFramework\`, developers can either implement this protocol themselves or use the default implementation /// provided by the library. The provider is passed to the library via the \`DataProviders\` class. /// /// - Note: Proper implementation of this protocol ensures accurate visualization of trading sessions and extended hours on the chart. public protocol ScheduleProvider { /// Fetches the complete trading schedule for a specific instrument within a given time range. /// /// This method retrieves the full trading schedule, including session breaks and extended hours, for a specific instrument. /// The data is returned as a \`Trading.Schedule\` object, which provides detailed information about trading sessions. /// /// - Parameters: /// - startTime: The start time of the requested range, in milliseconds since the Unix epoch. /// - endTime: The end time of the requested range, in milliseconds since the Unix epoch. /// - instrument: The financial instrument for which the trading schedule is requested. /// - completion: A closure that is called with the result of the operation. The result contains either a \`Trading.Schedule\` or an \`Error\` if the request fails. func fetchTradingSchedule( startTime: Int64, endTime: Int64, instrument: Instrument, completion: @escaping (Result) -> Void ) } @available(*, deprecated, renamed: "ScheduleProvider", message: "This typealias is deprecated and will be removed in version 1.4.0. Use \`ScheduleProvider\` instead.") /// A deprecated typealias for \`ScheduleProvider\`. /// /// This typealias is provided for backward compatibility and will be removed in a future version. /// Use \`ScheduleProvider\` directly instead. public typealias QDScheduleProtocol = ScheduleProvider `} ## Trading.Schedule Data struct for storing processed information about instrument's trading hours. {` public extension Trading { /// Data struct for storing processed information about instrument's trading hours. /// /// Trading hours are split by sessions [Schedule.Day.TradingSession]. /// /// Every session belongs to a day [Schedule.Day]. /// /// Sessions can be of different types [Schedule.Session.SessionType]. /// /// - Parameters: /// - name: Name of the instrument (e.g., "AAPL"). /// - timeZone: Time zone identifier of the instrument. /// - days: List of days [Schedule.Day]. public struct Schedule: Codable { public let name: String public let timeZone: String public let days: [Day] public struct Day: Codable { public var id: String public var isHoliday: Bool public var isShort: Bool public var sessions: [Trading.Session] } } /// Data class for storing processed information about instrument's trading hours for a single day. /// /// Trading hours are split by sessions [Trading.Session]. /// /// - Parameters: /// - id: ID of the day (e.g., "2021-11-08"). /// - isHoliday: Is the day a holiday with no trading. /// - isShort: Is the day a short day with shorter trading hours. /// - sessions: List of sessions [Trading.Session]. struct Session: Codable { public let startTime: Date public let endTime: Date public let type: SessionType public enum SessionType: String, Codable { case noTrading = "NO_TRADING" case preMarket = "PRE_MARKET" case regular = "REGULAR" case afterMarket = "AFTER_MARKET" } } } `} Extended hours are displayed on the chart as follows: ![image-2024-1-29_0-41-13](../../../images/extended_hours.png) \ ## Default provider If you haven't implemented a Schedule provider earlier and are using [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api) to retrieve data. Please contact your DXFeed sales manager or visit https://dxfeed.com/contact-sales/ for assistance. Default implementation code: {` import DXChart import DXFeedFramework struct DXFeedScheduleProvider: DXChart.ScheduleProvider { private let dataStorage = DataStorage.shared func fetchTradingSchedule( startTime: Int64, endTime: Int64, instrument: Instrument, completion: @escaping (Result) -> Void ) { do { guard let tradingHours = dataStorage.tradingHours(symbol: instrument.symbol) else { completion(.failure(ServerError.noData)) return } let schedule = try DXSchedule(scheduleDefinition: tradingHours) let startDay = try schedule.getDayByTime(time: startTime) let endDay = try schedule.getDayByTime(time: endTime) var days = [Trading.Schedule.Day]() for index in startDay.dayId...endDay.dayId { let day = try schedule.getDayById(day: index) let sessions: [Trading.Session] = day.sessions.compactMap { TradingSession( startTime: Date(timeIntervalSince1970: Double($0.startTime / 1000)), endTime: Date(timeIntervalSince1970: Double($0.endTime / 1000)), type: $0.type.sessionType ) } let dxcDay = Trading.Schedule.Day( id: "\(day.dayId)", isHoliday: day.holiday != 0, isShort: day.shortDay != 0, sessions: sessions ) days.append(dxcDay) } let dxcSchedule = DXChart.Trading.Schedule( name: instrument.symbol, timeZone: instrument.timezone, days: days ) completion(.success(dxcSchedule)) } catch { completion(.failure(ServerError.unknown)) osLogger.error("Error Fetching schedule: \(error)") } } } private extension ScheduleSessionType { var sessionType: Trading.Session.SessionType { switch self { case .noTrading: .noTrading case .preMarket: .preMarket case .regular: .regular case .afterMarket: .afterMarket @unknown default: fatalError() } } } `} --- ## Chart for iOS/Configuration/Settings/Settings.mdx import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; # Settings SettingsManager is responsible for managing settings in DXCharts. You can either provide your own SettingsManager or use the default implementation, DXCSettingsManager, provided by DXChart. To set custom values, you simply need to provide a custom instance of Chart.Settings models through the SettingsManager instance. ### Default settings | Setting | Default value | |--------------------|-----------------------------------------------------------------------| | chartType | .candle | | highAndLow | false | | verticalGrid | true | | horizontalGrid | false | | candleWicks | true | | watermark | false | | tradingFromChart | false | | showActiveOrders | false | | showOpenPositions | false | | autoscalePriceAxis | true | | fitStudies | true | | fitOrders | false | | fitPositions | false | | invertScale | false | | lockScale | false | | scaleType | .regular | | sessionBreaks | false | | extendedHours | false | | alignDataWithSessionStart | false | | priceType | .last | | volume | true | | eventsOnChart | false | | dividends | false | | splitsAndConsolidations | false | | earningsAndEstimates | false | | conferenceCalls | false | | news | false | ### Available settings: - chartType: candle, bar, line, area, scatterPlot, hollowCandle, histogram, baseline, trend, heikinAshi, equivolume. - scaleType: regular, percent, logarithmic. - priceType: last, market, bid, ask. --- ## Chart for iOS/Quick Start.mdx import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; # Quick start The documentation describes the algorithm for connecting the DXChart library for the iOS with basic functionality. ## Requirements - iOS 15.0+ - Real device or simulator to launch the app ## Demo project We provide a demo project with all default providers (based on DXFeedFramework) and default settings upon client request. ## Installation To install this package **locally**, follow these steps: 1. **Unzip the package:** ```sh unzip DXPackage.zip -d ``` 2. **Open your project in Xcode.** 3. **Add the local package:** - Go to `File` -> `Add Packages...` - Click on `Add Local...` - Navigate to the unzipped folder "DXPackage" and select it. 4. **Ensure the package is added to your target's dependencies.** ## Base framework configuration For prepare framework for work do this steps: 1. Import the `DXChart` module in your `UIApplicationDelegate`. {` import DXChart `} 2. Configure framework used a method `configure()` of `DXChartApp` in your app delegate's `application(_:didFinishLaunchingWithOprions:)` method: {` DXChartApp.configure() `} That's it! You can see an example about setup custom configuration ## Using the Chart ### Describe components You can use `ChartView` and `ChartScreen` components to present your data. - ChartView is a UIView component that you can integrate into your screens. It has an API for configuration and displaying data directly. (This component is available starting from version 1.2.0.") - ChartScreen is the UIViewController that includes all features of our framework. It requires some dependencies to work, such as CandlesProvider and IPFDataProvider. It encapsulates a huge part of the logic related to the chart and has more features than ChartView. ### Using ChartScreen This section describes how to integrate ChartScreen directly into your iOS application. You can provide Candle data to ChartScreen to display on the chart used CandlesProvider. If you need a service to retrieve market data, you can use [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api). The settings parameters are set to default values and persist changes during usage. To store and manage settings, ChartScreen uses a **SettingsManager**. You can provide your own instance of the manager or use the default implementation (DXCSettingsManager). To set up custom parameters, see [Settings](Chart-for-iOS/Configuration/Settings/Settings). ### Chart screen configuration To configure ChartScreen, you need to initialise it using the static method `makeScreen()` from `ChartScreen` enum. A ChartScreen instance requires the following mandatory dependencies: - dataProvider - provides all the necessary data for the chart.. - delegate - a delegate for handling trading operations (buy/sell). - shouldShowLogo - the flag to control whether the logo should be displayed on the chart (if provided). - dataStorage - a storage system for managing instrument and aggregation data. - settingsManager - a manager for handling chart settings - uniqueIdentifier - a unique identifier to differentiate contexts when using multiple charts simultaneously. (Required only if you need to display multiple charts with different data.) You can provide your custom provider or implement by example if you use [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api). To setting up default data providers see [Configuration](Chart-for-iOS/Configuration/Data-Providers/data-provider.mdx) There is interface for making UIViewController of ChartScreen: {` public enum ChartScreen { public static func makeScreen( dataProvider: DataProvider, delegate: QuoteOperationsDelegate, logoImage: UIImage?, shouldShowLogo: Bool, shouldShowToolbar: Bool = true, dataStorage: DataStorage = DXChartApp.dataStorage, settingsManager: SettingsManager = DXChartApp.settingsManager, uniqueIdentifier: String? = nil ) } `} ### [DataProvider](Chart-for-iOS/Configuration/Data-Providers/data-provider.mdx) To provide data to the chart, you need to implement a DataProvider that conforms to the DXChart.DataProvider protocol. You can use the default implementation built on DXFeed, which is available [here](Chart-for-iOS/Configuration/Data-Providers/data-provider.mdx). {` public protocol DataProvider { var candlesProvider: CandlesProvider { get } var quotesDataProvider: QuotesDataProvider { get } var ipfDataProvider: IPFDataProvider { get } var scheduleProvider: ScheduleProvider { get } var newsProvider: NewsProvider? { get } } `} ### [CandlesProvider](Chart-for-iOS/Configuration/Data-Providers/candles-provider.mdx) **CandlesProvider** — provides data with selected trading instrument's candles that needs to be displayed on the chart. You can use the default implementation built on DXFeed, which is available [here](Chart-for-iOS/Configuration/Data-Providers/candles-provider.mdx). {` import Combine /// Protocol for receiving candle data and managing the state of the connection. /// /// Provider that supplies data about candles for the selected trading instrument. The chart listens to \`candlesPublisher\`, /// a flow of candle data updates, and reacts to changes in it by updating the displayed information accordingly. /// Whenever there is a new candle data emitted by \`candlesPublisher\`, the chart updates to reflect this new data, /// ensuring that the visual representation of the trading instrument's price movements is always current and accurate. public protocol CandlesProvider { /// Publisher for receiving an array of \`Candle\` data. /// /// Provides the publisher of candle data for the requested symbol. var candlesPublisher: AnyPublisher<[Candle], Never> { get } /// Publisher for receiving the availability state of candle data. /// /// Emits \`true\` when data is available, \`false\` otherwise. var isDataAvailable: AnyPublisher { get } /// Publisher for receiving the loading state of candle data after parameters change. /// /// Emits \`true\` when candle data is being loaded, \`false\` when loading is complete. var isLoading: AnyPublisher { get } /// Publisher for receiving the current connection state. /// /// Tracks the state of the connection to the candle data provider. var connectionState: AnyPublisher { get } /// Updates the parameters for receiving candle data. /// /// - Parameters: /// - symbol: The instrument symbol for which the candle data is requested. /// - priceType: The type of price to use for the instrument (bid, ask, last, market). /// - aggregation: The aggregation settings for the candle data (time interval or granularity). /// - extendedHours: Flag indicating whether to include extended trading hours in the data. /// - alignSessionStart: Flag indicating whether to align data to the start of the trading session. func changeParams( symbol: String, priceType: PriceType, aggregation: Aggregation, extendedHours: Bool, alignSessionStart: Bool ) } `} ### [QuotesDataProvider](Chart-for-iOS/Configuration/Data-Providers/quotes-data-provider.mdx) **QuotesDataProvider** — provides data with selected trading instrument's quotes for the selected instrument, including Bid and Ask prices. You can use the default implementation built on DXFeed, which is available [here](Chart-for-iOS/Configuration/Data-Providers/quotes-data-provider.mdx). {` import Combine /// Interface for receiving quotes data used in the DXChart framework. /// /// When loading a chart in the DXChart framework, quotes data is sourced from this interface. /// /// - Use \`dataPublisher\` to supply quotes data. /// - Use \`changeSymbol\` to change the instrument symbol for which quotes are being provided. public protocol QuotesDataProvider { /// Publisher that provides quotes data. /// /// The quotes data is represented by the \`Quote\` structure. This publisher emits the latest \`Quote\` data, and a \`nil\` value if no data is available. var dataPublisher: AnyPublisher { get } /// Changes the instrument symbol for which quotes are being provided. /// /// - Parameter symbol: The instrument symbol (e.g., "AAPL", "TSLA") for which the quotes data should be fetched. func changeSymbol(_ symbol: String) } `} ### [IPFDataProvider](Chart-for-iOS/Configuration/Data-Providers/ipf-data-provider.mdx) **IPF Data Provider** - provides the list of symbols which user can select to see on the chart. DataProvider also should store IPF data provider which conform **IPFDataProvider protocol**. You can use the default implementation built on DXFeed, which is available [here](Chart-for-iOS/Configuration/Data-Providers/ipf-data-provider.mdx). {` import Combine /// Protocol for receiving instrument profile data - array of InstrumentData. /// /// When loading DXChart framework, an array of instruments, their descriptions, etc. are taken from this interface /// /// When connecting DXChart framework, developer can implement this interface or use the default implementation [link on the default implementation] and pass it to the framework using DXChart.DataProvider class public protocol IPFDataProvider { /// Publisher of receiving instrument profile data. /// /// Instrument profile data is represented by array of InstrumentData. var dataPublisher: AnyPublisher, Never> { get } /// Provide information about loading status. var isLoading: AnyPublisher { get } } `} ### [ScheduleProvider](Chart-for-iOS/Configuration/Data-Providers/schedule-provider.mdx) DataProvider also should store Schedule data provider which conform **ScheduleProtocol protocol**. **ScheduleProvider** - provides schedule for Extended Hours and Session Breaks. You can use the default implementation built on DXFeed, which is available [here](Chart-for-iOS/Configuration/Data-Providers/schedule-provider.mdx) {` /// A protocol that provides session breaks and extended hours data for the chart. /// /// The \`ScheduleProvider\` is responsible for supplying trading session breaks and extended hours information, /// which are used to highlight specific time ranges on the chart (e.g., market open/close times, breaks, or after-hours trading). /// /// When integrating with \`DXFeedFramework\`, developers can either implement this protocol themselves or use the default implementation /// provided by the library. The provider is passed to the library via the \`DataProviders\` class. /// You can find Default Implementation Documentation here https://devexperts.com/dxcharts/documentation-for-developers/Chart-for-iOS /// /// - Note: Proper implementation of this protocol ensures accurate visualization of trading sessions and extended hours on the chart. public protocol ScheduleProvider { /// Fetches the complete trading schedule for a specific instrument within a given time range. /// /// This method retrieves the full trading schedule, including session breaks and extended hours, for a specific instrument. /// The data is returned as a \`Trading.Schedule\` object, which provides detailed information about trading sessions. /// /// - Parameters: /// - startTime: The start time of the requested range, in milliseconds since the Unix epoch. /// - endTime: The end time of the requested range, in milliseconds since the Unix epoch. /// - instrument: The financial instrument for which the trading schedule is requested. /// - completion: A closure that is called with the result of the operation. The result contains either a \`Trading.Schedule\` or an \`Error\` if the request fails. func fetchTradingSchedule( startTime: Int64, endTime: Int64, instrument: Instrument, completion: @escaping (Result) -> Void ) } `} ### [NewsProvider](Chart-for-iOS/Configuration/Data-Providers/news-provider.mdx) **NewsProvider** responsible for news and events on chart. You should provide your custom **network manager** which should confirm **NewsProvider protocol**. Otherwise news and events wouldn't show on chart. Make your custom new provider which conform "NewsProvider protocol". You can provide your custom provider or implement by example if you use [`DXFeedFramework`](https://github.com/dxFeed/dxfeed-graal-swift-api). You can use the default implementation built on DXFeed, which is available [here](Chart-for-iOS/Configuration/Data-Providers/news-provider.mdx) {` import Combine /// A protocol for providing news data related to a specific financial instrument. /// /// The \`NewsProvider\` protocol defines the required functionalities for any provider that supplies /// news data, corporate actions, conference call information, and earnings data for a presented symbol. /// Conforming types must implement the necessary publishers to push relevant data updates. /// /// This protocol is useful for applications that need to display timely news and corporate information /// to users based on the selected financial instrument. public protocol NewsProvider { /// A publisher that emits an array of \`NewsItem\` instances related to the presented symbol. /// /// The publisher sends updates as a result of fetching news data, providing either /// a success case containing an array of news items or an error case if the request fails. var newsPublisher: AnyPublisher, Never> { get } /// A publisher that emits an array of \`CorporateAction\` instances. /// /// This publisher sends updates on corporate actions (e.g., stock splits, dividends) relevant to /// the presented symbol, similar to the \`newsPublisher\` in structure and functionality. var corporateActionPublisher: AnyPublisher, Never> { get } /// A publisher that emits an array of \`Earning\` instances related to conference calls. /// /// This publisher provides information about upcoming and past conference calls for the /// presented symbol, allowing consumers to stay informed about earnings announcements. var conferenceCallPublisher: AnyPublisher, Never> { get } /// A publisher that emits an array of \`Earning\` instances related to earnings reports. /// /// This publisher sends updates on earnings data for the symbol, helping consumers track performance /// and financial metrics associated with the instrument. var earningsPublisher: AnyPublisher, Never> { get } /// Updates the data for the specified financial instrument symbol. /// /// - Parameter symbol: The symbol for which to fetch and update news data. /// This method should be called whenever the user selects a different symbol to display /// its associated news and corporate actions. func updateData(for symbol: String) } `} ### QuoteOperationsDelegate If you need to handle Buy and Sell values, you should create your own class which will confirm QuoteOperationsDelegate protocol. Then you need to implement thees functions in your class: {` public protocol QuoteOperationsDelegate: AnyObject { func buyButtonWasPressed(_ value: Double?) func sellButtonWasPressed(_ value: Double?) } `} ### Additional features #### Logo image You can provide your **logo image** to be presented on the chart screen. To do it you should initialise ChartViewController with two additional parameters: - logoImage - requires UIImage; - shouldShowLogo - requires Bool value to determine whether to show the logo or not. #### uniqueIdentifier You can provide a uniqueIdentifier, and the DXChart framework will use it to handle this chart’s data separately, including mechanisms for dividing data such as drawings and indicator settings. #### Example of initialise ChartScreen: {` import UIKit import DXChart class Handler: DXChart.QuoteOperationsDelegate { func buyButtonWasPressed(_ value: Double?) { debugPrint(">>> HANDLER BUY sender.title = \(value ?? 0)") } func sellButtonWasPressed(_ value: Double?) { debugPrint(">>> HANDLER SELL sender.title = \(value ?? 0)") } } class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? private let quoteOperationsHandler = Handler() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } self.window = UIWindow(windowScene: windowScene) directStartWithChart() } private func directStartWithChart() { // like an example let dataProvider = CustomDataProvider( quoteAddress: <#quoteAddress#>, ipfAddress: <#ipfAddress#>, instrument: DXChartApp.dataStorage.instrument ) let chartScreenViewController = ChartScreen.makeScreen( dataProvider: dataProvider, delegate: quoteOperationsHandler, logoImage: nil, shouldShowLogo: false, shouldShowToolbar: true } window?.rootViewController = chartScreenViewController window?.makeKeyAndVisible() } } `} --- ## Getting started/_context-framework/context-example.tsx ```tsx import React, { FC } from 'react'; import { createRoot } from 'react-dom/client'; import { useSink } from '@dx-private/dxchart5-react/dist/utils/use-sink'; import { context } from '@dx-private/dxchart5-react/dist/context/context2'; { const ctx = context.of(1); const sink = ctx(null); console.log(sink.value); // prints 1 } { const one = context.of(1); const two = context.of(2); const three = context.combine(one, two, (o, t) => { return o + t; }); console.log(three(null).value); // prints 3 } { const sum = context.combine(context.key()('a'), context.key()('b'), (a, b) => { return a + b; }); const sink = sum({ a: 1, b: 4, }); console.log(sink.value); // prints 5 } { const op = context.combine( context.key()('a'), context.key()('b'), context.key()('op'), (a, b) => { return a + b; }, ); const mathPackage = context.combine(context.defer(op, 'op'), sum => { const calculateSum = () => { return sum({ op: '+' }).value; }; return { sum: calculateSum, }; }); const sink = mathPackage({ a: 1, b: 4, }); const math = sink.value; console.log(math.sum()); // prints 5 } // date formatter utils // combined component export interface Order { id: number; type: 'limit' | 'stop-limit' | 'market'; price: number; quantity: number; side: 'buy' | 'sell'; expiration: number; } interface OrderConfirmationProps { order: Order; priceFormatter: (price: number) => string; dateFormatter: (timestamp: number) => string; } export interface MathPackage { sum(a: number, b: number): number; toFixedPrecision(n: number, base: number): string; } export interface DateFormatter { format(timestamp: number): string; } export interface OrderProvider { getCurrentOrder(): Order; } export const OrderConfirmation: FC = (props: OrderConfirmationProps) => { const totalPrice = props.priceFormatter(props.order.price * props.order.quantity); return (

A {props.order.type} order to {props.order.side}

Expiration: {props.order.expiration}

Total price: {totalPrice}

); }; { const OrderConfirmationContainer = context.combine( context.key()('mathPackage'), context.key()('dateFormatter'), context.key()('orderProvider'), (mathPackage, dateFormatter, orderProvider) => { const currentOrder = orderProvider.getCurrentOrder(); const stocksPriceFormatter = (price: number) => { return mathPackage.toFixedPrecision(price, 2) + '$'; }; return ( ); }, ); // create some mock dependencies const orderProvider = { //@ts-ignore getCurrentOrder(): Order { // return order; }, }; const mathPackage = { sum(a: number, b: number): number { return a + b; }, toFixedPrecision(n: number, base: number): string { return n.toFixed(base); }, }; const defaultDateFormatter = { format(timestamp: number): string { return new Date(timestamp).toString(); }, }; const App: FC = () => { // here we useSink hook and pass in required dependencies // if you try to remove any required dependency (try it) - it won't compile // the dependencies should have correct name and type // on this level you can pass in different set of dependencies for your application // for example use a different date formatter or different texts provider for i18n const element = useSink( () => OrderConfirmationContainer({ orderProvider, mathPackage, dateFormatter: defaultDateFormatter, }), [], ); // result of resolving Sink here is JSX.Element return element; }; const rootEl = document.getElementById('root'); if (rootEl) { const root = createRoot(rootEl); root.render(); } } ``` --- ## Getting started/_context-framework/context.ts ```ts import { context } from '@dx-private/dxchart5-react/dist/context/context2'; const one = context.of(1); const two = context.of(2); const three = context.combine(one, two, (o, t) => { return o + t; }); console.log(three(null).value); // prints 3 ``` --- ## Getting started/_context-framework/order-confirmation-container.tsx ```tsx import { context } from '@dx-private/dxchart5-react/dist/context/context2'; import React from 'react'; import { createRoot } from 'react-dom/client'; import { DateFormatter, MathPackage, Order, OrderConfirmation, OrderProvider } from './context-example'; const OrderConfirmationContainer = context.combine( context.key()('mathPackage'), // specify dependency type and name context.key()('dateFormatter'), context.key()('orderProvider'), (mathPackage, dateFormatter, orderProvider) => { const currentOrder = orderProvider.getCurrentOrder(); const stocksPriceFormatter = (price: number) => { return mathPackage.toFixedPrecision(price, 2) + '$'; }; return ( ); }, ); // provide dependency instance here const orderProvider = { //@ts-ignore getCurrentOrder(): Order { // return undefined; }, }; const mathPackage = { sum(a: number, b: number): number { return a + b; }, toFixedPrecision(n: number, base: number): string { return n.toFixed(base); }, }; const dateFormatter = { format(timestamp: number): string { return new Date(timestamp).toString(); }, }; const OrderConfirmationComponent = OrderConfirmationContainer({ dateFormatter, // will not compile until all 3 dependencies are provided with correct type orderProvider, mathPackage, }); const root = createRoot(document.body); root.render(OrderConfirmationComponent.value); ``` --- ## Getting started/about-dxcharts.mdx --- name: About DXcharts sorting: 4 --- # About DXcharts 🔸 **Welcome to DXcharts**! 🔸 **DXcharts** is a developer-friendly charting library built for financial applications. Use it to visualize market data, analyze price movements, or build custom trading tools. DXcharts supports: - Over **100 technical indicators** - **40 drawing tools** - **11 chart types** You can use it for anything from simple price charts to detailed technical analysis. The library includes features like: - Placing **orders directly on charts** - **Comparing multiple instruments** side-by-side - Customizing **chart layouts** and **timeframes** - Creating **custom studies** using its own scripting language, dxScript DXcharts is **cross-platform**, working on **web**. The interface adapts to any device and is fully customizable, so you can tailor it to your project's look and workflow. Integration is designed to be straightforward. The library is **data feed-agnostic**, meaning you can connect it to any market data source you prefer, including dxFeed. Its **modular architecture** lets you include only the features you need. --- ## Getting started/context-framework.mdx --- name: Context framework sorting: 1 tags: architecture,internal --- import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; import Context from '!!raw-loader!./_context-framework/context.ts'; import OrderConfirmationContainer from '!!raw-loader!./_context-framework/order-confirmation-container.tsx'; # Context framework **DXcharts** manages state with `Context`, an open-source framework developed by **Devexperts**. It serves two main purposes: - State management (similar to `Redux`) - Code organization It defines dependencies and resolves them at **compile time** (not runtime) using `TypeScript` and the `fp-ts` library (functional programming methods). This approach helps catch errors early and improves type safety. If you have experience with Angular’s injector, `Context` works in a similar way, but with enhanced type safety. If you are familiar with `Spring` (Java), it is also similar to compile-time dependency resolution. Under the hood, `Context` is based on the **Reader monad** from `fp-ts` library. When evaluated, it returns a `Sink` containing both a **value** and any associated **effects**. --- ## 🔸 Basic example Here’s a simple example showing how to create and compose Context instances: {Context} This example demonstrates creating Context instances and combining them using `combine`. --- ## 🔸 Practical example The following example shows a more practical use case: {OrderConfirmationContainer} In this example, `OrderConfirmationContainer` is created using three dependencies defined with `context.key`. Until all dependencies are resolved with the correct types, you cannot create an instance of `OrderConfirmationContainer`. This ensures type safety during development. --- ## 🔸 Context utility functions Context is a monad and includes several utility functions to support functional programming patterns: - `sequenceT`: Combines multiple contexts into a single context. - `sequenceArray`: Combines an array of contexts into a single context. - `chain`: Chains contexts sequentially. - `flatten`: Flattens nested contexts. - `map`: Maps a value within a context. - `of`: Wraps a value in a context. --- --- ## Getting started/navigating-this-documentation.mdx --- name: Navigating this documentation sorting: 2 --- # Navigating this documentation This documentation is organized by **package** and **platform**, with dedicated sections for each major integration type. Use the sidebar to find: - Quick start guides - Configuration references - API documentation --- ## 🔸 DXcharts Lite - GitHub repository: Source code and contribution info - [Quick start](../dxcharts-lite/quick-start): Core concepts and setup for HTML5/Canvas integration - [Chart configuration](../dxcharts-lite/configuration/chart-configuration): Guide to customizing chart behavior and appearance - [API reference](../dxcharts-lite/api-reference/overview): Methods and events for Canvas-based charts --- ## 🔸 DXcharts Library (React) - [Quick start](../dxcharts-react/quick-start): Setup and usage in React projects - [Zip](../dxcharts-react/setup-and-integration/zip): Install packages from a `ZIP` file - [Providers](../dxcharts-react/providers/overview): Context and data providers for React - [API reference](../dxcharts-react/api-reference/overview): Methods and events - [Styling](../dxcharts-react/configuration/styling): Use styled-components for custom appearance --- ## 🔸 DXcharts Widget - [Script](../dxcharts-react/widget/script): Embed via ``} Then create new `ChartBootstrap` using `DXChart` global `ChartBootstrap` is a constructor function, it accepts 2 parameters: - `element` - HTML container element - `config` (optional) - instance of [[Chart Config]] > Also, please set `width: 100%` and `height:100%` for parent container > by default chart is auto-resizing to parent > you can change it by setting `fixedSize` in config Now you should have empty chart on screen ## Set data Let's pass in some data i.e. `Candles`. You can use bundled function to generate some mock data. Also, you would need `Instrument` object - it is required to show instrument description on chart and show correct price precisions on Y axis. Now you should see chart just like this one Here is full quick-start code example ```markup
``` --- ## dxcharts-lite/QuickStart.js ```js export const createChart = () => { const container = document.getElementById('chart_container'); const chart = new DXChart.ChartBootstrap(container); return chart; }; export const generateMockData = () => { const mockCandles = DXChart.generateCandlesData(); const mockInstrument = { symbol: 'AAPL', priceIncrements: [0.01], }; chart.setData(mockCandles, mockInstrument); }; ``` --- ## dxcharts-lite/QuickStart.ts ```ts /* eslint-disable */ import { Chart } from '@devexperts/dxcharts-lite/dist'; export const createChart = () => { const container = document.getElementById('chart_container') as HTMLElement; const chart = new Chart(container); return chart; }; ``` --- ## dxcharts-lite/Scaling.mdx --- tags: scaling --- import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import ScaleHistory from '!!raw-loader!./code/ScaleHistory.ts'; import ScaleHistoryItem from '!!raw-loader!../../../../chart-core/src/chart/model/scale.model.ts'; import { FlexContainer } from '../../components/ui/FlexContainer'; import DragExplanation from './img/drag_explanation.png'; import scaleByAxisImg from './img/scaleByAxis.gif'; import scaleByScrollImg from './img/scaleByScroll.gif'; import scaleByScrollLockedImg from './img/scaleByScrollLocked.gif'; import panningChartImg from './img/panningChart.gif'; import ChartConfig from '!!raw-loader!../../../../chart-core/src/chart/chart.config.ts'; import { RenderDedokTable } from '../../components/dedok/DedokTable/DedokTable'; # Scaling Scaling is the process of changing charts scale i.e. start/end of X and Y axis You can scale chart by dragging X and Y axis. Scale by axis Also, you can use mouse scroll to change chart's scale: Scale by mouse scroll with X You can enable `lockPriceToBarRatio` and scale X and Y axis both: Scale by by mouse scroll with X and Y # Panning Panning is the process of changing charts position (up, down, right, left). Just press left mouse button, and drag chart: Scale by by mouse scroll with X and Y # Scale History `chart`'s `ScaleModel` has a public `history` property, which is used to store history of applied scales. This store is `stack`-like data structure and the elements are `ScaleHistoryItem`'s. Since this is a `stack`, so to manipulate this history, `ScaleModel` has a couple of methods: # For example: # Auto Scale Disable OnDrag Interface This config allows you to disable auto scale when you do a long drag in the vertical direction ## Auto Scale Disable OnDrag Interface Description When you start drag on the chart, the `ChartAreaPanHandler` checks three conditions: 1. `AutoScaleDisableOnDrag.enabled === true` 1. `AutoScaleDisableOnDrag.edgeAngle > α` 1. `AutoScaleDisableOnDrag.yDiff > AB length` If all of them are true the auto-scale will be disabled Horizontal Ray styled example # Candles Viewport There is a possibility to configure chart candles viewport via `viewportStrategy` option inside ChartConfig, which allows to show the direct timeframe or the same candles amount regardless of the chosen instrument or period. Choose `timeframe` property to save the current timeframe, `candles` to save the exact amount of candles or `basic` to do the basic scale. ### Chart Scale Config ### Chart React Viewport Config --- ## dxcharts-lite/Session Breaks.mdx --- tags: session-breaks --- import { SessionBreaks, SessionBreaksChange, SessionBreaksColored } from './code/SessionBreaks.tsx' import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import SessionBreaksCode from '!!raw-loader!./code/SessionBreaks.tsx'; # Session Breaks The session breaks component allows to create vertical zones with borders showing difference between sessions when market is opened or closed > To enable session breaks make them visible (**false** by default) ## Highlighting Zones To draw session breaks create highlighting zones model and set it to ```HighlightsComponent```; the model requires * **type** - any string (```PRE_MARKET``` and ```AFTER_MARKET``` by default) to identify your zone * **from** - timestamp from, will stick to closest candle * **to** - timestamp to # ## AM/PM Label And Border * **label.text** - add a label on top * **label.placement** - identify align placement of label * **border.left** - true to show left border * **border.right** - true to show right border ## Custom Zones And Single Line You may add custom zone **type** which will have stored color configuration For example say you want a ```PRE_MARKET``` special zone in yellow and ```AFTER_MARKET``` vertical line in purple After configured in **config.colors** you may use new highlights in your model # --- ## dxcharts-lite/Studies.mdx import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import Studies from '!!raw-loader!../../../../chart-core-modules/modules/studies/studies.config.ts'; # Studies configuration Studies configuration is defined in `config.components.studies` with following interface: --- ## dxcharts-lite/Styling Colors.mdx --- tags: styling --- import { RenderDedokTable } from '../../components/dedok/DedokTable/DedokTable'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import Colors from '!!raw-loader!./code/Colors.tsx'; # Chart Colors Change colors of chart during initialization or using API later on Here is the `colors` object description --- ## dxcharts-lite/Styling-Gradient.mdx --- tags: styling --- import { Gradient } from './code/Gradient.tsx'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import ChartAreaTheme from '!!raw-loader!../../../../chart-core/src/chart/chart.config.ts'; import Colors from '!!raw-loader!./code/Colors.tsx'; # Gradient Background Color DXCharts supports setting **gradient** color to its background. It can be achieved by changing the config listed below: By default chart's background mode is set to **regular** and **backgroundColor** variable configures it's color. To make it gradient simply change the mode to **gradient** and override **backgroundGradientTopColor** and **backgroundGradientBottomColor** variables just as for [[Styling Colors|Colors]]: ## Gradient Background Color Example --- ## dxcharts-lite/Theme Builder - Lite.mdx import { ChartThemeBuilder } from '../../components/chart-theme-builder/chart-theme-builder'; ## Chart Lite Theme Builder Playground The playground allows to try different custom coloring for the chart lite application. ## Further reading - [[Theme Builder|dxchart5-react Theme Builder]] --- ## dxcharts-lite/Timezones - Core.mdx --- tags: timezone --- import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import Timezone from '!!raw-loader!./code/timezones.ts'; # Chart Timezone DXCharts has API responsible for the timezone manipulation and configuration. New API is implemented with the help of new TimeZoneModel, which provides the following methods: ## Get Timezone value in milliseconds There is method to get the timezone offset value in milliseconds: ## Changing Timezone To change timezone use `chart.timeZoneModel.setTimeZone` API from any of the list above ## Subscribing to timezone change Timezone model contains observable, which could be used to execute a callback after timezone is changed: ## Format date and time You can completely override date and time string with your own using `initFormatterFactory` function, which takes the `DateFormatter` object to format values. By default it takes the [chart config](dxcharts-lite/Chart-Config) dateFormatter property ## Further reading - [[Timezones|Timezones in dxcharts-react]] - [Chart config](dxcharts-lite/Chart-Config) --- ## dxcharts-lite/Unmount.mdx --- tags: internal --- import { ChartUnmountComponent } from './code/ChartUnmountComponent.tsx'; import ChartUnmountComponentCode from '!!raw-loader!./code/ChartUnmountComponent.tsx'; import ChartDestroyCode from '!!raw-loader!./code/ChartUnmount.ts'; import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; # Chart Unmount If you don't need the chart in you host app you can use `destroy()` method ## Unmount Example # {ChartUnmountComponentCode} --- ## dxcharts-lite/Watermark.mdx --- tags: watermark --- import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import ChartConfig from '!!raw-loader!../../../../chart-core/src/chart/chart.config.ts'; import Watermark from '!!raw-loader!./code/Watermark.ts'; # Watermark Watermark allows to protect content and to claim ownership of something. In addition to that, Watermark can be used to show system messages. > You can change watermark visibility using watermark component API
## Watermark Configuration Settings of watermark such as font family, font size, offsets, paddings are also able to be configured in **_ChartConfig.ts_**. There is also a possibility to override the time interval formatter to display aggregation period differently. ## Further reading - [[WaterMarkComponent|Watermark Component API]] - [Watermark in dxcharts-react](dxcharts-react/Watermark/Overview) --- ## dxcharts-lite/X-Axis.mdx --- tags: x-labels --- import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import XAxis from '!!raw-loader!./code/XAxis.tsx'; import day_1 from './img/day_1.png'; import day_3 from './img/day_3.png'; import day_5 from './img/day_5.png'; import customXRegularLabels from './img/customXRegularLabels.png'; # X axis The axis is automatically created based on the label config and data (candles) The main configurations you would probably like to look into are - Label Config (Time formatting) - Appearance (font and padding) ## Labels generation algorithm There are different types of labels like **year**, **month**, **day**, **year**, **hour**, **minute**, **second**, **lessThenSecond** and **week-weekday**. Some are higher-level then the others, for example year > month and etc. - this means that if same candle matches year and month - year will take precedence. They are all configured with a config that has a default view: There are intermediate labels for some types. For **minute** it's **5min**, **15min** and **30min** auxiliary labels which take precedence over **1min** labels. Same **4h**, **12h** steps for **hour** and **3M** (quarter-year), **6M** (half-year) for **month** Labels start generating from top-level ones if they are applicable for current period selected (see table below). While generating they try to position evenly across X axis. X time labels are generated based on selected **aggregation period** and follow these rules: | Name | Condition | Default format | Example | | ------- | ------------------------------------------------- | -------------- | ------- | | year | period >= 1day | `YYYY` | 2020 | | month | period >= 1day | `MMM` | Jul | | weekday | any period but only for last week candles | `EEE` | Wed | | day | any period but only for **non**-last week candles | `dd.MM` | 08.07 | | hour | period < 1day | `HH:mm` | 10:00 | | minute | period < 1day | `HH:mm` | 10:02 | ## European / American Format If you want to change format of month candles to american format for example you need to pass this config Each key of this dictionary (except for **lessThenSecond**) has a mandatory digit in the postfix that specifies the rule for a particular label to appear on the X scale and the date format that the label will accept. Numbers in the postfix can take integer values for **second**, **minute** - from 1 to 60 **hour** - from 1 to 24 **day** - from 1 to 31 **month** - from 1 to 12 **year** - from 1 and any higher. ## Example of configuring To change this configuration, use the config or the API The appearance rule using the **day** label as an example: For a configure that looks like this schematically, the X scale will look like: In case **day_1** - for each candle the date of which is divisible by **1**(everyday) will be created a label with the specified formatting. In case **day_3** - a label with the specified formatting will be created for each candle whose date is divisible by **3**. As you have noticed this rule is based on the periodicity of the appearance of divisible dates, but if you want to highlight one particular date, add the symbol **!** after the number, for example: It is possible to combine settings with the same label types, and a setting with a larger number in the postfix will overwrite a setting with a smaller number if their rules coincide on the same date(candle). For other types of labels, these rules work exactly the same way. ## week-weekday label This rule is created to mark a periodic label depending on the number of the week in the month and the day of the week, it has 2 numbers in the postfix separated by **\_**. The first number means the number of the week in the month and the second number the day of the week. For example **week-weekday_3_5** will display every **third Friday** of the month. The range of valid values for the postfix for this label is 1 to 6 for the first number (and a special symbol $ to indicate the last week) and 1 to 7 for the second number. ## European / american format If you want to change the date format for each day to american format, add this option to your config: ## Appearance (font and padding) You may change font of labels using Extra space padding from top and bottom ## Labels length How you can achieve X-scale labels to be more evenly positioned? Here's the default format config for x scale labels: The thing is, that **x-labels-generator** takes into account the maximum possible length of a label, i.e number of symbols. For example in default config the longest format is **HH:mm:ss**, which is 8 symbols long. Bearing that in mind, **x-labels-generator** will try to position every possible label as if it has maximum possible length. And it stops on the first label, that satisfies all the requirements. That's why labels can be positioned not evenly. You can overcome that by reducing the maximum possible length of a label. For example, if you know that you don't have seconds aggregation, and you don't need that long format like **HH:mm:ss**, just delete it. i.e that's may be your config: This will help you to make maximum possible length of a label more stable in average, in this example it will be 5 symbols in average. And because of that chart will be able to place labels more evenly. ## Custom labels Let's imagine, that all configuration above is not suitable for you. By default `dxcharts-lite` uses default `DateTimeFormatter` while working with dates. But be careful, this formatter is used not only to format XAxis labels but in a whole `dxcharts-lite` lib. You can override default formatter by providing your own implementation: After overriding `DateTimeFormatter` you could see new labels on XAxis: --- ## dxcharts-lite/Y-Axis.mdx --- tags: y-labels --- import { PriceLabelsOverview } from './code/pricel-labels-overview'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import PartialChartConfig from '!!raw-loader!./code/pricel-labels-overview'; import { PriceLabelModes } from './code/price-labels-mode'; import PriceLabelModesCode from '!!raw-loader!./code/price-labels-mode'; import ChartConfig from '!!raw-loader!../../../../chart-core/src/chart/chart.config.ts'; import { PriceLabelTypes } from './code/price-labels-type'; import PriceLabelTypesCode from '!!raw-loader!./code/price-labels-type'; import { PriceLabelsVisibility } from './code/price-labels-visibility'; import PriceLabelsVisibilityCode from '!!raw-loader!./code/price-labels-visibility'; import VolumeLabelCode from '!!raw-loader!./code/volume-label'; import { VolumeLabelComponent } from './code/volume-label'; # Overview The axis is automatically created based on the data (candles). You can configure different label types for Y Axis (high/low, for instance) - see [price labels overview](#priceLabelsMode), [price labels mode](#priceLabelsType) and [price labels visibility](#priceLabelsVisibility). ## API reference The main functionality of Y Axis Component is: - Switch Axis visibility - Configure visibility of different price label types - Register your own price label provider [YAxisComponent API](/chart/api-reference/YAxisComponent) # Price Labels DXchart can show a bunch of different price labels on Y axis. You can find related API in the `YAxisComponent`, which is available from the `chart`. ## Example # # Price Labels Mode Price labels have a different draw `modes` available. Also, if 'label' or 'line-label' mode is chosen, it's possible to configure label's appearance with 'type' property. (more here: [Price Label Type](/chart/y-axis/price-labels/type)) Moreover, you can display the `descriptions` of the labels. You can provide initial `descriptions` state and `mode` for each `type` of labels via `chartConfig` Or you can change it dynamically via `chart` ## Playground # Price Labels Type Each price label has a config to determine its type and set some additional settings for each type: Type property allows to configure visual appearance of label, there are four of them: 1. Badge: rectangle with a triangle at the end of it 2. Rectangle - basic rectangle 3. Plain - Absense of visual form, only text is displayed Also it's possible to set rounded corners for rectangle and badge types ## Playground # Price Label Visibility You can modify the visibility state of each `type` of price labels. You can provide initial `visible` state for each `type` of labels via `chartConfig` Or you can change it dynamically via `chart` ## Playground # Volume Label in order to enable the display of the volume label on the chart, the volume display should be enabled and the corresponding settings must be set as shown in the config in the example below. You can change volume label visibility using `chart` method like in example below ## Playground --- ## dxcharts-lite/code/ChartCompare.tsx ```tsx import React, { useCallback, useRef } from 'react'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { DemoChart } from '../../../components/utils/DemoChart'; import { generateCandles } from '@dx-private/dxchart5-react/dist/utils/generator/candle-generator.utils'; import { ChartBootstrap } from '../../../components/utils/chart.model'; export const ChartCompare = () => { const chartRef = useRef(); const onChartCreated = useCallback( (chart: ChartBootstrap) => { chartRef.current = chart; chartRef.current.chartComponent.setSecondarySeries({ candles: generateCandles(), instrument: { symbol: 'MOCK', description: 'MOCK', }, }); chartRef.current.chartComponent.setSecondarySeries({ candles: generateCandles(), instrument: { symbol: 'MOCK2', description: 'MOCK2', }, }); chartRef.current.yAxis.setAxisType('percent'); }, [chartRef], ); return ( <>
); }; ``` --- ## dxcharts-lite/code/ChartExamples.tsx ```tsx import React, { useRef, useCallback } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { BarType } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { PartialChartConfig } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { ChartBootstrap } from '../../../components/utils/chart.model'; const makeChart = (chartType: BarType) => { const style = { paddingRight: 30, paddingTop: 20, paddingBottom: 20, }; const names = { candle: 'Candle', bar: 'Bar', area: 'Area', line: 'Line', baseline: 'Baseline', hollow: 'Hollow candle', trend: 'Trend', histogram: 'Histogram', heikinAshi: 'Heikin-Ashi', scatterPlot: 'Scatter Plot', }; return (
{names[chartType]} chart
); }; export const TypeCandle = () => makeChart('candle'); export const TypeBar = () => makeChart('bar'); export const TypeLine = () => makeChart('line'); export const TypeArea = () => makeChart('area'); export const TypeHA = () => makeChart('heikinAshi'); export const TypeHistogram = () => makeChart('histogram'); export const TypeHollow = () => makeChart('hollow'); export const TypeBaseline = () => makeChart('baseline'); export const TypeScatterplot = () => makeChart('scatterPlot'); export const TypeTrend = () => makeChart('trend'); export const NavigationMap = () => { const localChart = useRef(); const onChartCreated = useCallback((chart: ChartBootstrap) => { localChart.current = chart; }, []); return ( <>
); }; export const chartWithConfig = (name: string, config?: PartialChartConfig) => () => { const chartRef = useRef(); const onChartCreated = useCallback((chart: ChartBootstrap) => { chartRef.current = chart; }, []); return ( <>
{name}
); }; export const ThinGrid = chartWithConfig('Thin grid', { components: { grid: { dash: [1, 2], horizontal: true } } }); export const ThickDashedGrid = chartWithConfig('Thick dashed grid', { components: { grid: { dash: [5, 5], width: 2, horizontal: true } }, }); export const VerticalOnly = chartWithConfig('Vertical only (default)'); export const Highlights = () => { const localchart = useRef(); const onChartCreated = useCallback((chart: ChartBootstrap) => { localchart.current = chart; if (localchart) { localchart.current?.highlights.setHighlightsVisible(true); localchart.current?.highlights.setHighlights([ { type: 'PRE_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, label: { text: 'Market Closed', placement: 'right-left', }, }, { type: 'AFTER_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, label: { text: 'E', placement: 'right-right', }, border: { left: true, }, }, ]); } }, []); return ( <>
); }; export const Minimal = () => { const localchart = useRef(); const onChartCreated = useCallback((chart: ChartBootstrap) => { localchart.current = chart; }, []); return ( <>
); }; export const MiniChart = (props: any) => { const { width, height } = props; const miniChartConfig: PartialChartConfig = { fixedSize: { height, width, }, components: { chart: { type: 'line', defaultZoomCandleWidth: 1, }, navigationMap: { visible: false, }, xAxis: { visible: false, }, yAxis: { visible: false, }, offsets: { visible: false, }, volumes: { visible: false, }, grid: { visible: false, }, }, }; return ; }; ``` --- ## dxcharts-lite/code/ChartExport.ts ```ts /* eslint-disable */ import { ChartDataExporter } from '@dx-private/dxchart5-react/dist/chart/containers/chart-export-data/ChartDataExporter'; export const onClickHandler = (chartDataExporter: ChartDataExporter) => { chartDataExporter.exportChartData(); }; ``` --- ## dxcharts-lite/code/ChartExportComponent.tsx ```tsx import React, { useRef, useCallback } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { ChartDataExporter } from '@dx-private/dxchart5-react/dist/chart/containers/chart-export-data/ChartDataExporter'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { ChartBootstrap } from '../../../components/utils/chart.model'; export const ChartExportComponent = () => { const localchart = useRef(); const onChartCreated = useCallback((chart: ChartBootstrap) => { localchart.current = chart; }, []); const exportData = () => { if (localchart.current !== undefined) { const chartDataExporter = new ChartDataExporter( localchart.current.chartModel, localchart.current.studies.model, periodInSecondsFormatter(localchart.current.chartModel.getPeriod()), ); chartDataExporter.exportChartData(); } }; return ( <>
); }; /** * Formats period in ms to human-readable string. * @param periodInMs * @doc-tags utility,period */ export const periodInSecondsFormatter = (periodInMs: number): string => { const yearInMs = 29030400000; const month = yearInMs / 12; const week = month / 4; const day = week / 7; const hour = day / 24; const minute = hour / 60; if (periodInMs < minute) { return `${periodInMs}s`; } if (periodInMs >= minute && periodInMs < hour) { return `${Math.floor(periodInMs / minute)}m`; } if (periodInMs >= hour && periodInMs < day) { return `${Math.floor(periodInMs / hour)}h`; } if (periodInMs >= day && periodInMs < week) { return `${Math.floor(periodInMs / day)}d`; } if (periodInMs >= week && periodInMs < month) { return `${Math.floor(periodInMs / week)}w`; } if (periodInMs >= month && periodInMs < yearInMs) { return `${Math.floor(periodInMs / month)}m`; } return `${Math.floor(periodInMs / yearInMs)}y`; }; ``` --- ## dxcharts-lite/code/ChartUnmount.ts ```ts import { Chart } from '@devexperts/dxcharts-lite/dist'; export const chartDestroy = (chart: Chart) => { chart.destroy(); }; ``` --- ## dxcharts-lite/code/ChartUnmountComponent.tsx ```tsx import { generateCandles } from '@dx-private/dxchart5-react/dist/utils/generator/candle-generator.utils'; import { Chart } from '@devexperts/dxcharts-lite/dist'; import React, { useCallback, useRef } from 'react'; import { FlexContainer } from '../../../components/ui/FlexContainer'; const setCandles = (ci: Chart) => { const generatedCandles = generateCandles(); ci.setData({ candles: generatedCandles, }); }; export const ChartUnmountComponent = () => { const localChart = useRef(); const containerRef = useRef(null); const mountChart = useCallback(() => { if (containerRef.current) { const chart = new Chart(containerRef.current, { fixedSize: { height: 440, width: 790, }, }); setCandles(chart); localChart.current = chart; } }, []); const unmountChart = useCallback(() => { if (localChart.current) { localChart.current.destroy(); localChart.current = undefined; } }, []); return ( <>
); }; ``` --- ## dxcharts-lite/code/Colors.tsx ```tsx import React, { useRef, useCallback } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { PartialChartConfig } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { Chart } from '@devexperts/dxcharts-lite/dist'; const DARK_COLORS = { chartAreaTheme: { gridColor: 'rgba(255,255,255,0.2)', backgroundColor: 'rgba(27,32,33,1)', foregroundColor: 'rgba(153,153,153,1)', axisColor: 'rgba(255,255,255,0.4)', mapFillColor: 'rgba(35,44,45,1)', mapGradientTopColor: 'rgba(35,44,45,1)', mapGradientBottomColor: 'rgba(35,44,45,1)', volumeFillColor: 'rgba(57,61,61,0.3)', volumeLineColor: 'rgba(98,106,107,1)', }, candleTheme: { bullColor: 'rgba(5,196,129,1)', bearColor: 'rgba(247,70,79,1)', noneColor: 'rgba(128,128,128,1)', bullWickColor: 'rgba(128,128,128,1)', bearWickColor: 'rgba(128,128,128,1)', }, waterMarkTheme: { firstRowColor: "rgba(255,255,255,0.3)", secondRowColor: "rgba(255,255,255,0.3)", thirdRowColor: "rgba(255,255,255,0.3)" }, barTheme: { bullColor: 'rgba(51,194,142,1)', bearColor: 'rgba(255,69,84,1)', noneColor: 'rgba(128,128,128,1)', }, lineTheme: { bullColor: 'rgba(51,194,142,1)', bearColor: 'rgba(255,69,84,1)', noneColor: 'rgba(128,128,128,1)', }, areaTheme: { lineColor: 'rgba(127,120,214,1)', startColor: 'rgba(169,38,251,1)', stopColor: 'rgba(169,38,251,0.8)', }, }; export const DarkColors = () => { const localChart = useRef(); const onChartCreated = useCallback((chart: Chart) => { localChart.current = chart; localChart.current?.setColors(DARK_COLORS); }, []); return ( <>
); }; const ACID_COLORS = { chartAreaTheme: { gridColor: 'rgba(255,255,255,0.2)', backgroundColor: 'rgba(27,32,33,1)', foregroundColor: 'rgba(153,153,153,1)', axisColor: 'rgba(189,0,255,1)', mapFillColor: 'rgba(35,44,45,1)', mapGradientTopColor: 'rgba(0,184,255,0.5)', mapGradientBottomColor: 'rgba(35,44,45,1)', volumeFillColor: 'rgba(0,184,255,0.5)', volumeLineColor: 'rgba(189,0,255,1)', }, candleTheme: { bullColor: 'rgba(0,255,159,1)', bearColor: 'rgba(214,0,255,1)', noneColor: 'rgba(128,128,128,1)', bullWickColor: 'rgba(128,128,128,1)', bearWickColor: 'rgba(128,128,128,1)', }, waterMarkTheme: { firstRowColor: "rgba(255,255,255,0.3)", secondRowColor: "rgba(255,255,255,0.3)", thirdRowColor: "rgba(255,255,255,0.3)" }, barTheme: { bullColor: 'rgba(51,194,142,1)', bearColor: 'rgba(255,69,84,1)', noneColor: 'rgba(128,128,128,1)', }, lineTheme: { bullColor: 'rgba(51,194,142,1)', bearColor: 'rgba(255,69,84,1)', noneColor: 'rgba(128,128,128,1)', }, areaTheme: { lineColor: 'rgba(127,120,214,1)', startColor: 'rgba(169,38,251,1)', stopColor: 'rgba(169,38,251,0.8)', }, }; export const AcidColors = () => { const localChart = useRef(); const onChartCreated = useCallback((chart: Chart) => { localChart.current = chart; localChart.current?.setColors(ACID_COLORS); }, []); return ( <>
); }; export const changeColors = () => { const config: PartialChartConfig = { colors: { /* */ }, }; const element: HTMLElement = document.createElement('div'); const chart = new Chart(element, config); return chart; }; export const setColors = () => { const colors = { /* */ }; const element: HTMLElement = document.createElement('div'); const chart = new Chart(element); chart.setColors(colors); return chart; }; ``` --- ## dxcharts-lite/code/CrossTool.ts ```ts import { Chart } from '@devexperts/dxcharts-lite/dist'; import { MagnetTarget } from '@devexperts/dxcharts-lite/dist/chart/components/cross_tool/cross-tool.component'; export const changeCrossToolVisibility = (chart: Chart, visible: boolean) => { chart.crosshair.setVisible(visible); }; export const setCrossToolMagnet = (chart: Chart, snapMode: MagnetTarget) => { chart.crosshair.setMagnetTarget(snapMode); }; // supported only for 'cross-and-labels' type. export const setCrossToolMagnetTarget = (chart: Chart, target?: MagnetTarget) => { const availableTargets: MagnetTarget[] = ['C', 'H', 'L', 'O', 'OHLC', 'none']; chart.crosshair.setMagnetTarget(target ?? availableTargets[0]); }; ``` --- ## dxcharts-lite/code/CrossToolFullConfig.tsx ```tsx import React from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { ChartConfigComponentsCrossTool } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; export const fullCrossToolConfig: ChartConfigComponentsCrossTool = { type: 'cross-and-labels', discrete: false, magnetTarget: 'none', lineDash: [4, 6], xAxisLabelFormat: [ { format: 'dd.MM.YYYY', showWhen: { periodMoreThen: 24 * 60 * 60 * 1000, }, }, { format: 'dd.MM.YYYY HH:mm', showWhen: { periodLessThen: 24 * 60 * 60 * 1000, periodMoreThen: 6 * 1000, }, }, { format: 'dd.MM.YYYY HH:mm:ss', showWhen: { periodLessThen: 6 * 1000, }, }, ], xLabel: { padding: { top: 4, bottom: 4, right: 8, left: 8, }, margin: { top: 4, }, }, yLabel: { padding: { top: 4, bottom: 4, end: 4, start: 4, }, type: 'badge', }, }; export const CrossToolChart = () => { const config = { components: { crossTool: fullCrossToolConfig } }; return ; }; ``` --- ## dxcharts-lite/code/CrossToolXAxisConfig.ts ```ts import { getDefaultConfig } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; const config = getDefaultConfig(); config.components.crossTool.xAxisLabelFormat = [{ format: 'dd.MM HH:mm' }]; ``` --- ## dxcharts-lite/code/CrossToolXAxisFormat.ts ```ts export default { xAxisLabelFormat: [ { format: 'dd.MM', showWhen: { periodMoreThen: 24 * 60 * 60 * 1000, }, }, { format: 'HH:mm', showWhen: { periodLessThen: 24 * 60 * 60 * 1000, periodMoreThen: 6 * 1000, }, }, { format: 'HH:mm:ss', showWhen: { periodLessThen: 6 * 1000, }, }, ], }; ``` --- ## dxcharts-lite/code/Gradient.tsx ```tsx import { ColorPicker } from '@dx-private/dxchart5-react/dist/chart-kit/ColorPicker/ColorPicker.component'; import { DEFAULT_CHART_CONFIG } from '@dx-private/dxchart5-react/dist/config/chart-config'; import { DEFAULT_COLOR_PALETTE } from '@dx-private/dxchart5-react/dist/config/color-palette'; import React, { useCallback, useState } from 'react'; import styled from 'styled-components'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { DemoChart } from '../../../components/utils/DemoChart'; export const Gradient = () => { const [topColor, setTopColor] = useState('rgba(224,150,0)'); const [bottomColor, setBottomColor] = useState('rgba(118,42,88)'); const config = { ...DEFAULT_CHART_CONFIG, fixedSize: { height: 440, width: 790, }, themes: { ...DEFAULT_CHART_CONFIG.themes, dark: { ...DEFAULT_CHART_CONFIG.themes.dark, chartAreaTheme: { ...DEFAULT_CHART_CONFIG.themes.dark.chartAreaTheme, backgroundGradientTopColor: topColor, backgroundGradientBottomColor: bottomColor, }, }, light: { ...DEFAULT_CHART_CONFIG.themes.light, chartAreaTheme: { ...DEFAULT_CHART_CONFIG.themes.light.chartAreaTheme, backgroundGradientTopColor: topColor, backgroundGradientBottomColor: bottomColor, }, }, }, }; const onTopColorChangeHandler = useCallback((value: string) => { value && setTopColor(value); }, []); const onBottomColorChangeHandler = useCallback((value: string) => { value && setBottomColor(value); }, []); return ( <> Change background gradient colors
); }; export const ColorPickerContent = styled.div` margin-bottom: var(--spacer-2); `; export const ColorPickerWrapper = styled.span` margin-right: var(--spacer-1); `; ``` --- ## dxcharts-lite/code/Grid.ts ```ts import { Chart } from '@devexperts/dxcharts-lite/dist'; export const changeGridVisibility = (chart: Chart, visible: boolean) => { chart.setGridVisible(visible); chart.setGridVertical(visible); chart.setGridHorizontal(visible); }; export const setGridConfig = (chart: Chart) => { // you can also configure grid display options such as width and dash chart.setGridConfig({ dash: [5, 5], width: 2, horizontal: true, visible: true, vertical: true }); }; ``` --- ## dxcharts-lite/code/MagnifyingTools.ts ```ts import { ChartWithDrawings } from '@dx-private/dxchart5-modules/dist/drawings/drawings.config'; import { MagnifyingToolRectangleProperties } from '@dx-private/dxchart5-modules/dist/drawings/tools/MagnifyingToolRectangle'; import { MagnifyingToolTimeRangeProperties } from '@dx-private/dxchart5-modules/dist/drawings/tools/MagnifyingToolTimeRange'; import { drawingsDictionary } from '@dx-private/dxchart5-react/dist/config/localization/drawings'; export interface NewMagnifyingwDrawingConfig { id?: string; type: 'magnifying_tool_rectangle' | 'magnifying_tool_time_range' | 'magnifying_tool_time_range_wheel'; properties: MagnifyingToolTimeRangeProperties | MagnifyingToolRectangleProperties; } export const createNewMagnifyingwDrawing = (chart: ChartWithDrawings, localization: typeof drawingsDictionary) => { // config for a new magnifying drawing const magnifyingToolDrawing: NewMagnifyingwDrawingConfig = { id: '1', type: 'magnifying_tool_rectangle' || 'magnifying_tool_time_range' || 'magnifying_tool_time_range_wheel', properties: { background: { fillStyle: 'rgba(250,102,255,1)', }, projection: { lineColor: 'rgba(250,102,255,1)', lineWidth: 1, lineDash: [6, 6], }, text: { textBg: 'rgba(51,51,51,1)', textSize: '11px', backgroundOffsetWidth: 6, backgroundOffsetHeight: 3, }, line: { lineColor: 'rgba(250,102,255,1)', lineWidth: 1, lineDash: [], }, }, }; // starts the magnifying drawing chart.drawings.startDrawing(magnifyingToolDrawing); // then select with that drawing desired area on the chart and voila, the chart will make a zoom-in }; ``` --- ## dxcharts-lite/code/MagnifyingToolsComponent.tsx ```tsx import React, { useRef, useState, useCallback } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { ChangeEvent } from 'react'; import { ScalingDrawingType } from '@dx-private/dxchart5-react/dist/chart/model/drawing.model'; import { ChartBootstrap } from '../../../components/utils/chart.model'; const magnifyingToolProps = { line: { lineColor: 'rgba(250,102,255,1)', lineWidth: 1, lineDash: [6, 6], }, background: { fillStyle: 'rgba(250,102,255,1)', }, projection: { lineColor: 'rgba(250,102,255,1)', lineWidth: 1, lineDash: [6, 6], }, text: { textBg: 'rgba(51,51,51,1)', textSize: '14px', backgroundOffsetWidth: 6, backgroundOffsetHeight: 3, }, }; export const MagnifyingToolsComponent = () => { const localChart = useRef(); const [magnifyingType, setMagnifyingType] = useState('magnifying_tool_time_range'); const onChartCreated = useCallback((chart: ChartBootstrap) => { localChart.current = chart; chart.drawings.setMagnetMode(false); }, []); const onSelectMagnifyingToolType = useCallback( (event: ChangeEvent) => { const type = event.target.value; if (isScalingDrawing(type)) { setMagnifyingType(type); } }, [setMagnifyingType], ); const zoomOutHandler = useCallback(() => { const chart = localChart.current; if (chart) { chart.drawings.model.cancelDrawing(); chart.scale.popFromHistory(); const scaleHistory = chart.scale.history; if (scaleHistory.length) { const prevHistoryItem = { ...scaleHistory[scaleHistory.length - 1] }; if (scaleHistory.length === 1) { chart.scale.popFromHistory(); } chart.scale.setXScale(prevHistoryItem.xStart, prevHistoryItem.xEnd); chart.scale.setYScale(prevHistoryItem.yStart, prevHistoryItem.yEnd); chart.scale.recalculateZoom(); } } }, []); const zoomInHandler = useCallback(() => { const chart = localChart.current; if (chart) { chart.drawings.model.cancelDrawing(); const currentHistory = chart.scale.history; // TODO rework if (currentHistory.length === 0) { const currentScale = { xStart: chart.scale.xStart, xEnd: chart.scale.xEnd, yStart: chart.scale.yStart, yEnd: chart.scale.yEnd, }; chart.scale.pushToHistory(currentScale); } chart.drawings.startDrawing({ type: magnifyingType, properties: magnifyingToolProps, }); } }, [magnifyingType]); return ( <> ); }; function isScalingDrawing(type: string): type is ScalingDrawingType { return ['magnifying_tool_time_range', 'magnifying_tool_rectangle'].includes(type); } ``` --- ## dxcharts-lite/code/NavigationMap.tsx ```tsx import React from 'react'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { DemoChart } from '../../../components/utils/DemoChart'; export const NavigationMapWithTimeLabels = () => { return ( ); }; ``` --- ## dxcharts-lite/code/Panes.tsx ```tsx import React, { useRef, useCallback } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { Chart } from '@devexperts/dxcharts-lite/dist'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { DataSeriesPoint } from '@devexperts/dxcharts-lite/dist/chart/model/data-series.model'; import { generateCandles } from '@dx-private/dxchart5-react/dist/utils/generator/candle-generator.utils'; import { DataSeriesConfig, DataSeriesType } from '@devexperts/dxcharts-lite/dist/chart/model/data-series.config'; const dataPoints: DataSeriesPoint[] = generateCandles().map(c => ({ timestamp: c.timestamp, close: c.close, })); export const PanesExample = () => { const localChart = useRef(); const onChartCreated = useCallback( (chart: Chart) => { localChart.current = chart; window['__CHART_INSTANCE'] = chart; createAndAddPaneWithDataPoints(chart, 'customPaneDefault', dataPoints, 0); createAndAddPaneWithDataPoints(chart, 'customPaneDefault1', dataPoints, 0); createAndAddPaneWithDataPoints(chart, 'customPaneDefault2', dataPoints, 0); createAndAddPaneWithDataPoints(chart, 'customPaneDefault3', dataPoints, 0); createAndAddPaneWithDataPoints(chart, 'customPane2D', transformDataPointsInto2DArrayWithGaps(dataPoints)); const panesAmount = chart.canvasBoundsContainer.panesOrder.length; const singlePaneRatio = 1 / panesAmount; const newHeightRatio = {}; Object.keys(chart.canvasBoundsContainer.graphsHeightRatio).forEach( key => (newHeightRatio[key] = singlePaneRatio), ); chart.canvasBoundsContainer.overrideChartHeightRatios(newHeightRatio); }, [localChart], ); return ( ); }; const createAndAddPaneWithDataPoints = ( chart: Chart, paneUUID: string, points: DataSeriesPoint[][] | DataSeriesPoint[], paneOrder?: number, ) => { const pane = chart.paneManager.createPane(paneUUID, { order: paneOrder, useDefaultHighLow: true }); const dataSeriesModel = pane.createDataSeries(); dataSeriesModel.dataPoints = points; }; const transformDataPointsInto2DArrayWithGaps = (points: DataSeriesPoint[]): DataSeriesPoint[][] => { const pointsCopy = points.slice(); const res: DataSeriesPoint[][] = []; const splitSize = 10; while (pointsCopy.length) { const part = pointsCopy.splice(0, splitSize); part.shift(); res.push(part); } return res; }; export const createPane = (chart: Chart) => { return chart.paneManager.createPane(); }; export const setData = (chart: Chart, points: DataSeriesPoint[], paneUUID: string) => { const pane = chart.paneManager.panes[paneUUID]; const dataSeriesModel = pane.createDataSeries(); dataSeriesModel.setDataPoints(points); }; export const updateDataSeriesConfig = (config: DataSeriesConfig, type: DataSeriesType) => { config.type = type; }; ``` --- ## dxcharts-lite/code/ScaleHistory.ts ```ts import { ScaleHistoryItem } from '@devexperts/dxcharts-lite/dist/chart/model/scale.model'; export class ScaleModel { // removes from the stack the last ScaleHistoryItem public popFromHistory(): ScaleHistoryItem | undefined { return; } // pushes to the stack new ScaleHistoryItem public pushToHistory(item: ScaleHistoryItem): void {} // removes all the elements from the history public clearHistory(): void {} } export function scaleHistoryExample(scale: ScaleModel) { const historyItem = scale.popFromHistory(); if (historyItem) { scale.pushToHistory(historyItem); scale.clearHistory(); } } ``` --- ## dxcharts-lite/code/SessionBreaks.tsx ```tsx import React, { useState, useRef, useCallback, useEffect } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { Highlight, HighlightTextPlacement, } from '@devexperts/dxcharts-lite/dist/chart/components/highlights/highlights.model'; import { ChartBootstrap } from '../../../components/utils/chart.model'; import { FullChartConfig } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; export const SessionBreaks = () => { const localChart = useRef(); const onChartCreated = useCallback((chart: ChartBootstrap) => { localChart.current = chart; if (localChart) { localChart.current?.highlights.setHighlightsVisible(true); localChart.current?.highlights.setHighlights([ { type: 'PRE_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, }, { type: 'AFTER_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, }, ]); } }, []); return ( <>
); }; export const SessionBreaksChange = () => { const localChart = useRef(); const [leftBorder, setLeftBorder] = useState(false); const [rightBorder, setRightBorder] = useState(false); const [labelPlacement, setLabelPlacement] = useState('left-right'); const [HighlightData, setHighlightData] = useState({ type: 'AFTER_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, border: { right: rightBorder, left: leftBorder, }, label: { text: 'AM', placement: labelPlacement, }, }); const onChartCreated = useCallback( (chart: ChartBootstrap) => { localChart.current = chart; if (localChart) { localChart.current?.highlights.setHighlightsVisible(true); localChart.current?.highlights.setHighlights([HighlightData]); } }, [HighlightData], ); const setHighlightsBorderLeftHandler = (): void => { setLeftBorder(!leftBorder); }; const setHighlightsBorderRightHandler = (): void => { setRightBorder(!rightBorder); }; const setLabelPlacementHandler = (event: any): void => { setLabelPlacement(event.target.value); }; useEffect(() => { setHighlightData(HighlightData => ({ ...HighlightData, border: { ...HighlightData.border, left: leftBorder, }, })); }, [leftBorder]); useEffect(() => { setHighlightData(HighlightData => ({ ...HighlightData, border: { ...HighlightData.border, right: rightBorder, }, })); }, [rightBorder]); useEffect(() => { if (HighlightData.label?.text) { setHighlightData(HighlightData => ({ ...HighlightData, label: { text: HighlightData.label?.text, placement: labelPlacement, }, })); } }, [labelPlacement, HighlightData.label?.text]); useEffect(() => { if (localChart.current) { localChart.current?.setHighlightsData([HighlightData]); localChart.current?.setHighlightsVisible(true); } }, [HighlightData]); let inputs; if (HighlightData.border) { inputs = ( <>
Label:
); } return ( <> {inputs} ); }; export const SessionBreaksColored = () => { const config = { colors: { highlights: { PRE_MARKET: { border: '#e9efc5', background: 'rgba(255,219,153,0.35)', label: 'rgba(255, 0, 0)', }, AFTER_MARKET: { border: '#800080', background: 'rgba(255,219,153,0.35)', label: 'rgba(255, 0, 0)', }, }, }, }; const localChart = useRef(); const onChartCreated = useCallback((chart: ChartBootstrap) => { localChart.current = chart; if (localChart) { localChart.current?.highlights.setHighlightsVisible(true); localChart.current?.highlights.setHighlights([ { type: 'PRE_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, label: { text: 'Market Closed', placement: 'right-left', }, }, { type: 'AFTER_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, label: { text: 'E', placement: 'right-right', }, border: { left: true, }, }, ]); } }, []); return ( <>
); }; export const changeHighlightsTovisible = (config: FullChartConfig) => { return (config.components.highlights.visible = true); }; export const setHighlights = (chart: ChartBootstrap) => { const highlights: Highlight[] = [ { type: 'PRE_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, }, { type: 'AFTER_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, }, ]; chart.highlights.setHighlights(highlights); }; export const setHighlightsWithLabel = (chart: ChartBootstrap) => { const highlights: Highlight[] = [ { type: 'AFTER_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, label: { text: 'AM', placement: 'left-right', }, }, ]; chart.highlights.setHighlights(highlights); }; export const colorConfig = { colors: { highlights: { PRE_MARKET: { border: '#e9efc5', background: 'rgba(255,219,153,0.35)', label: 'rgba(255, 0, 0)', }, AFTER_MARKET: { border: '#800080', background: 'rgba(255,219,153,0.35)', label: 'rgba(255, 0, 0)', }, }, }, }; export const setHighlightsWithBorder = (chart: ChartBootstrap) => { const highlights: Highlight[] = [ { type: 'PRE_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, label: { text: 'Market Closed', placement: 'right-left', }, }, { type: 'AFTER_MARKET', from: Date.now() - 1800000, to: Date.now() - 600000, label: { text: 'E', placement: 'right-right', }, border: { left: true, }, }, ]; chart.highlights.setHighlights(highlights); }; ``` --- ## dxcharts-lite/code/Watermark.ts ```ts import { Chart } from '@devexperts/dxcharts-lite/dist'; export const changeWaterMarkVisibility = (chart: Chart, visible: boolean) => { chart.watermark.setWaterMarkVisible(visible); }; export const setWaterMarkData = (chart: Chart) => { // you can display three rows of data using watermark chart.watermark.setWaterMarkData({ firstRow: 'AAPL', secondRow: 'Apple Inc.', thirdRow: undefined, }); }; export const setWaterMarkConfig = (chart: Chart) => { chart.watermark.setWaterMarkConfig({ visible: true, fontFamily: 'Open Sans, sans-serif', firstRowFontSize: 80, firstRowBottomPadding: 10, secondRowFontSize: 40, secondRowBottomPadding: 25, thirdRowFontSize: 40, thirdRowBottomPadding: 15, position: 'center', offsetX: 20, offsetY: 20, logoWidth: 20, logoHeight: 20, }); }; ``` --- ## dxcharts-lite/code/XAxis.tsx ```tsx import React, { useState, useRef, useCallback, ChangeEvent } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { ChartBootstrap } from '../../../components/utils/chart.model'; import { getDefaultConfig } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { TimeFormatWithDuration } from '@devexperts/dxcharts-lite/dist/chart/components/x_axis/time/parser/time-formats.model'; export const OverlappingChart = () => { const [overlapping, setOverlapping] = useState(50); const chartRef = useRef(); const onChartCreated = useCallback( (chart: ChartBootstrap) => { chartRef.current = chart; }, [chartRef], ); const onChange = (e: ChangeEvent) => { setOverlapping(parseInt(e.target.value, 10)); }; return ( <> Overlapping distance: ); }; export const config: Partial> = { lessThanSecond: 'mm:ss', minute_1: 'HH:mm', hour_1: 'HH:mm', day_1: 'dd.MM', month_1: 'MMM', year_1: 'YYYY', }; export const setFormatsForLabelsConfig = (chart: ChartBootstrap, config: Record) => { // using API chart.xAxis.setFormatsForLabelsConfig(config); }; export const exampleN1 = { day_1: 'dd', }; export const exampleN2 = { day_3: 'dd', }; export const exampleN3 = { day_3: 'dd', 'day_5!': 'dd', }; export const exampleN4 = { day_4: 'MM.dd', }; export const changeFont = () => { const config = getDefaultConfig(); config.components.xAxis.fontSize = 10; config.components.xAxis.fontFamily = 'Open Sans, sans-serif'; }; export const changePaddings = () => { const config = getDefaultConfig(); config.components.xAxis.padding.top = 8; config.components.xAxis.padding.bottom = 16; }; export const initialChartConfig = { components: { xAxis: { formatsForLabelsConfig: { lessThanSecond: 'mm:ss', second_1: 'HH:mm:ss', minute_1: 'HH:mm', minute_5: 'HH:mm', minute_30: 'HH:mm', hour_1: 'HH:mm', day_1: 'dd.MM', month_1: 'MMM', year_1: 'YYYY', }, }, }, }; export const chartConfigExample = { components: { xAxis: { formatsForLabelsConfig: { minute_1: 'HH:mm', minute_5: 'HH:mm', minute_30: 'HH:mm', hour_1: 'HH:mm', day_1: 'dd.MM', month_1: 'MMM', year_1: 'YYYY', }, }, }, }; export const customFormatterConfigExample = { dateFormatter: { // formatter works with pattern string like "HH.mm.ss" => "12:46:33" createFormatterFunction: (pattern: string) => (dateTimestamp: number) => { const date = new Date(dateTimestamp); const hour = date.getHours(); const minute = date.getMinutes(); const seconds = date.getSeconds(); const day = date.getDay(); const month = date.getMonth(); const year = date.getFullYear(); const formattedDate = pattern .replace('yyyy', `${year}`) .replace('MM', month < 10 ? `☢0${month}` : `☢${month}`) .replace('dd', day < 10 ? `↗0${day}` : `↗${day}`) .replace('HH', hour < 10 ? `☀0${hour}` : `☀${hour}`) .replace('mm', minute < 10 ? `☔0${minute}` : `☔${minute}`) .replace('s', seconds < 10 ? `☘0${seconds}` : `☘${seconds}`); return formattedDate; }, }, }; ``` --- ## dxcharts-lite/code/dx-feed-integration.tsx ```tsx import { createDxFeedProviderWithAdapter } from '@dx-private/dxchart-dx-feed-data-provider/dist/provider/dxfeed-data-provider'; import { ChartCandleData } from '@dx-private/dxchart5-react/dist/providers/chart-data-provider'; import { Candle, generateCandleId } from '@devexperts/dxcharts-lite/dist/chart/model/candle.model'; import { uuid } from '@devexperts/dxcharts-lite/dist/chart/utils/uuid.utils'; import React, { useRef, useState } from 'react'; import { ChartBootstrap } from '../../../components/utils/chart.model'; import { DemoChart } from '../../../components/utils/DemoChart'; export const DxFeedChartDataIntegration = () => { const localchart = useRef(); const dxFeedConfig = { endpointUrl: 'wss://tools.dxfeed.com/webservice/cometd', // you might need an auth token if you use a different endpoint // authToken: 'token', }; const [chartDataProvider] = useState(() => createDxFeedProviderWithAdapter(dxFeedConfig)); const onChartCreated = (chart: ChartBootstrap) => { localchart.current = chart; const toCandles = (data: ChartCandleData): Candle => ({ id: generateCandleId(data.time, data.high + data.low + data.open + data.close), hi: data.high, lo: data.low, open: data.open, close: data.close, timestamp: data.time, volume: data.volume, impVolatility: data.impVolatility, vwap: data.vwap, typicalPrice: data.typicalPrice, }); const symbol = 'AAPL'; const aggregation = { duration: 1, durationType: 'h' } as const; chartDataProvider.requestHistoryData(symbol, aggregation).then(chartData => { chart.setData({ candles: chartData.map(toCandles), instrument: { symbol, priceIncrements: [], }, }); chart.chartModel.doBasicScale(); }); const subId = uuid(); chartDataProvider.subscribeCandles(symbol, aggregation, subId, data => { chart.chartComponent.updateCandles(data.map(toCandles), symbol); }); }; return ( ); }; ``` --- ## dxcharts-lite/code/price-labels-mode.tsx ```tsx import React, { useRef, useState } from 'react'; import { PartialChartConfig, YAxisLabelMode } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { DEFAULT_CHART_CONFIG } from '@dx-private/dxchart5-react/dist/config/chart-config'; import { DemoChart } from '../../../components/utils/DemoChart'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { ChartBootstrap } from '../../../components/utils/chart.model'; const demoChartConfig: PartialChartConfig = { ...DEFAULT_CHART_CONFIG, fixedSize: { height: 400, width: 600, }, components: { yAxis: { labels: { descriptions: false, settings: { lastPrice: { mode: 'none', type: 'badge', }, }, }, }, }, }; export const PriceLabelModes = () => { const [selectedMode, selectMode] = useState('none'); const [descriptions, toggleDescriptions] = useState(false); const localchart = useRef(); if (demoChartConfig?.components?.yAxis?.labels?.settings?.lastPrice) { demoChartConfig.components.yAxis.labels.settings.lastPrice.mode = selectedMode; } if (demoChartConfig?.components?.yAxis?.labels) { demoChartConfig.components.yAxis.labels.descriptions = descriptions; } const onChartCreated = (chart: ChartBootstrap) => { localchart.current = chart; }; const handleChangeMode = (e: any) => { selectMode(e.target.value); }; const handleChangeDescrptions = () => { toggleDescriptions(visible => !visible); }; return (
); }; export const config: PartialChartConfig = { components: { yAxis: { labels: { descriptions: false, settings: { lastPrice: { mode: 'label', type: 'rectangle', }, }, }, }, }, }; export const changeYAxisState = (chart: ChartBootstrap) => { // usage chart.yAxis.changeLabelMode('lastPrice', 'line-label'); chart.yAxis.changeLabelsDescriptionVisibility(true); }; ``` --- ## dxcharts-lite/code/price-labels-type.tsx ```tsx import React, { useRef, useState } from 'react'; import { PartialChartConfig, YAxisLabelAppearanceType } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { DEFAULT_CHART_CONFIG } from '@dx-private/dxchart5-react/dist/config/chart-config'; import { DemoChart } from '../../../components/utils/DemoChart'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { ChartBootstrap } from '../../../components/utils/chart.model'; const demoChartConfig: PartialChartConfig = { ...DEFAULT_CHART_CONFIG, fixedSize: { height: 400, width: 600, }, components: { yAxis: { labels: { descriptions: false, settings: { lastPrice: { mode: 'label', type: 'badge', }, }, }, }, }, }; export const PriceLabelTypes = () => { const [selectedType, selectType] = useState('badge'); const [rounded, toggleRounded] = useState(false); const localchart = useRef(); if (demoChartConfig?.components?.yAxis?.labels?.settings?.lastPrice) { demoChartConfig.components.yAxis.labels.settings.lastPrice.type = selectedType; } if (demoChartConfig?.components?.yAxis?.typeConfig) { //@ts-ignore demoChartConfig.components.yAxis.typeConfig[selectedType].rounded = rounded; } const onChartCreated = (chart: ChartBootstrap) => { localchart.current = chart; }; const handleChangeType = (e: any) => { selectType(e.target.value); }; const handleChangeRounded = () => { toggleRounded(rounded => !rounded); }; return (
); }; export const config: PartialChartConfig = { components: { yAxis: { typeConfig: { badge: { rounded: true, paddings: { top: 4, bottom: 4, start: 6, end: 6, }, }, plain: {}, rectangle: { rounded: false, paddings: { top: 4, bottom: 4, start: 6, end: 6, }, }, }, labels: { descriptions: false, settings: { lastPrice: { mode: 'label', type: 'rectangle', }, }, }, }, }, }; ``` --- ## dxcharts-lite/code/price-labels-visibility.tsx ```tsx import { PartialChartConfig, YAxisLabelType } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { DEFAULT_CHART_CONFIG } from '@dx-private/dxchart5-react/dist/config/chart-config'; import React, { useRef, useState } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { ChartBootstrap } from '../../../components/utils/chart.model'; export const PriceLabelsVisibility = () => { const [types, changeTypes] = useState, boolean>>({ lastPrice: true, countdownToBarClose: false, drawings: false, }); const labelMode = (enabled: boolean) => (enabled ? 'line' : 'none'); const demoChartConfig: PartialChartConfig = { ...DEFAULT_CHART_CONFIG, fixedSize: { height: 340, width: 490, }, components: { yAxis: { labels: { descriptions: true, settings: { lastPrice: { mode: labelMode(types.lastPrice), }, }, }, }, }, }; const localchart = useRef(); const onChartCreated = (chart: ChartBootstrap) => { localchart.current = chart; }; const handleChange = (e: any) => { const type = e.target.value; changeTypes(types => ({ ...types, [type]: !types[type], })); }; return ( <>
); }; export const config: PartialChartConfig = { components: { yAxis: { labels: { descriptions: true, settings: { lastPrice: { mode: 'label', }, }, }, }, }, }; export const changeLabelVisibility = (chart: ChartBootstrap) => { chart.yAxis.changeLabelMode('lastPrice', 'label'); }; ``` --- ## dxcharts-lite/code/pricel-labels-overview.tsx ```tsx import { PartialChartConfig } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { LabelGroup, LabelsGroups, VisualYAxisLabel } from '@devexperts/dxcharts-lite/dist/chart/components/y_axis/price_labels/y-axis-labels.model'; import { DEFAULT_CHART_CONFIG } from '@dx-private/dxchart5-react/dist/config/chart-config'; import React, { useCallback } from 'react'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { DemoChart } from '../../../components/utils/DemoChart'; import { ChartBootstrap } from '../../../components/utils/chart.model'; const config: PartialChartConfig = { ...DEFAULT_CHART_CONFIG, fixedSize: { height: 400, width: 600, }, components: { yAxis: { labels: { descriptions: true, settings: { lastPrice: { mode: 'label', }, prevDayClose: { mode: 'label', }, }, }, }, }, }; export const PriceLabelsOverview = () => { const onChartCreated = useCallback((chart: ChartBootstrap) => { const getUnorderedLabels = (): LabelGroup[] => { const pdcLabels: LabelGroup[] = []; const pdcVisualLabel: VisualYAxisLabel = { labelText: chart.chartModel.pane.valueFormatter(350), bgColor: chart.config.colors.labels.prevDayClose.boxColor, textColor: chart.config.colors.labels.prevDayClose.textColor, y: chart.chartModel.toY(350), description: 'Pre', mode: chart.config.components.yAxis.labels.settings.prevDayClose.mode, }; pdcLabels.push({ labels: [pdcVisualLabel], }); return pdcLabels; }; chart.yAxis.registerYAxisLabelsProvider( { getUnorderedLabels }, LabelsGroups.MAIN, 'prev_day_close', ); }, []); return } ``` --- ## dxcharts-lite/code/timezones.ts ```ts import { Chart } from '@devexperts/dxcharts-lite/dist'; export const getTimeZoneOffset = (chart: Chart, timezone: string) => { return chart.timeZoneModel.tzOffset(timezone); }; export const setTimeZone = (chart: Chart, timezone: string) => { chart.timeZoneModel.setTimeZone(timezone); }; export const observeTimeZoneChanged = (chart: Chart) => { return chart.timeZoneModel.observeTimeZoneChanged(); }; ``` --- ## dxcharts-lite/code/volume-label.tsx ```tsx import { attachVolumeLabel } from '@dx-private/dxchart5-modules/dist/volume-label/index'; import { ChartWithVolumeLabel } from '@dx-private/dxchart5-modules/dist/volume-label/volume-label.config'; import React, { useRef, useState } from 'react'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { DemoChart } from '../../../components/utils/DemoChart'; import { ChartBootstrap } from '../../../components/utils/chart.model'; import { setDataSeries } from '../Data-Series/examples'; export const VolumeLabelComponent = () => { const [visible, setVisible] = useState(true); const localChart = useRef(); const onChartCreated = (chart: ChartBootstrap) => { attachVolumeLabel(chart); // eslint-disable-next-line no-restricted-syntax localChart.current = chart as ChartBootstrap & ChartWithVolumeLabel; localChart.current?.volumeLabel.setVisible(visible); setDataSeries(chart); }; localChart.current?.volumeLabel.setVisible(visible); const handleChange = (e: React.ChangeEvent) => { const visible = e.target.checked; setVisible(visible); }; return ( <>
); }; export const changeVolumeLabelVisibility = (chart: ChartWithVolumeLabel, visible: boolean) => { chart.volumeLabel.setVisible(visible); }; ``` --- ## dxcharts-lite/dxFeed Integration - Core.mdx --- tags: dxfeed --- import { DxFeedChartDataIntegration } from './code/dx-feed-integration'; import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; import code from '!!raw-loader!./code/dx-feed-integration.tsx'; # DxFeed Chart Data Provider integration with Chart-Core In this example we use dx-feed-provider from package '@dx-private/dxchart-dx-feed-data-provider' {code} --- ## dxcharts-lite/multiple scales/Overview.md import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration.tsx'; import { MDXH4HR } from '../../../mdx-config/MDXH4HR.tsx'; import { DedokMethod } from '../../../components/dedok/DedokMethod/DedokMethod.tsx'; import { YExtents } from './live-examples.tsx'; import examples from '!!raw-loader!./examples.ts'; # Overview By default `dxcharts-lite` has only one y-axis (scale) and a pane is also created with only one y-axis. However, you can add as many y-axis as you want. To use this feature you need to use a concept called `Extent`. Technically, `Extent` is a container for `y-axis` component, `ScaleModel`, and other tiny components. ### API usage #### Add a new extent and series #### Merge extents into one #### Live example --- ## dxcharts-lite/multiple scales/examples.ts ```ts import { Chart } from '@devexperts/dxcharts-lite/dist'; import { generateCandlesDataTS } from '@devexperts/dxcharts-lite/dist/chart/utils/candles-generator-ts.utils'; export const addExtentAndSeries = (chart: Chart) => { const pane = chart.paneManager.panes.CHART; const newExtent = pane.createExtentComponent(); const dataSeries = newExtent.createDataSeries(); dataSeries.dataPoints = generateCandlesDataTS({ quantity: 1000 }); // updateView is necessary to recalculate internal state after the data were set pane.updateView(); }; export const mergeAllExtentsIntoOne = (chart: Chart) => { chart.paneManager.panes.CHART.mergeYExtents(); }; ``` --- ## dxcharts-lite/multiple scales/live-examples.tsx ```tsx import React, { useRef } from 'react'; import { DemoChart } from '../../../components/utils/DemoChart'; import { Chart } from '@devexperts/dxcharts-lite/dist'; import { addExtentAndSeries, mergeAllExtentsIntoOne } from './examples'; export const YExtents = () => { const chartRef = useRef(); return ( <> (chartRef.current = ci)} /> ); }; ``` --- ## dxcharts-react/API Reference - API.mdx import { DedokMethods } from '../../components/dedok/DedokMethods/DedokMethods.tsx'; # API Reference - API This page shows API which is available to chart users. ### ChartReactSupportedAPI Here goes the `supported` API reference: --- ## dxcharts-react/API Reference - Configuration.mdx import { RenderDedokTable } from '../../components/dedok/DedokTable/DedokTable'; # API Reference - Configuration --- ## dxcharts-react/API Reference - Providers.mdx --- tags: data,config --- import { RenderDedokTable } from '../../components/dedok/DedokTable/DedokTable'; import { DedokMethods } from '../../components/dedok/DedokMethods/DedokMethods'; # API Reference - `dxchart5-react` Providers Providers are a way to integrate data with outside world - host application ## Mandatory providers To load candles and chart service data (Quotes, Trades, ...) ## Additional features providers --- ## dxcharts-react/Advanced/Architecture.mdx # `dxchart5-react` Architecture Here you can read about main architecture concepts that are used in dxchart5-react. ## View models View Models hold and control the state, encapsulate business logic Usually any widget has a VM which holds it's state as close to UI as possible. When implementing React components we usually try not to introduce any local state and lift it on VM level. The state from VM (or VMs) is collected/processed/converted and passed to components as props in containers. VM's are just dependencies which have their reactive state (RxJS). The magic here is: whenever RxJS state fires a new event to subject - the container (i.e. component) is re-rendered. So you can reliably control the state in VM's and be sure that all components will re-render whenever the state in VM changes. --- ## dxcharts-react/Advanced/Internal API.mdx import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import ChartReactInternalAPI from '!!raw-loader!../../../../../chart-react/src/chart/view-models/api/chart-react-api.view-model.ts'; import ReactAppState from '!!raw-loader!../../../../../chart-react/src/chart/view-models/api/chart-react-api.view-model.ts'; import ChartReactDebug from '!!raw-loader!../../../../../chart-react/src/chart/view-models/api/chart-react-api.view-model.ts'; # Internal API If you haven't found method that you need from Supported API, you can try to use Internal API. Internal API gives you access to all `dxchart5-react` internal codebase. --- ## dxcharts-react/Advanced/Sync state with external storage.mdx import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods.tsx'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import LocalStorageLayoutProvider from '!!raw-loader!./code/localstorage-layout-provider.ts'; import LocalStorageUserDataProvider from '!!raw-loader!./code/localstorage-userdata-provider.ts'; # Sync state with server Most of the time you want to store user progress/data somewhere in a database for a better user experience. In `dxchart5-react` you can achieve such functionality via a couple of data [[Quick Start - Providers|providers]]. [Layout Provider](dxcharts-react/Providers/layout-provider) and [UserDataProvider](dxcharts-react/Providers/user-data-provider) are main providers that used to save application state and user data and sync it to some external storage like `localstorage` or some client's `database`. That's pretty much it! ## Further reading [[Quick Start - Providers|Providers]] --- ## dxcharts-react/Advanced/code/localstorage-layout-provider.ts ```ts import { ChartLayoutData, ChartLayoutNamed, ChartLayoutWithId, LayoutProvider, } from '@dx-private/dxchart5-react/dist/providers/layout-provider'; import { replaceInArray } from '@devexperts/dxcharts-lite/dist/chart/utils/array.utils'; import { DEFAULT_THEME } from '@dx-private/dxchart5-react/dist/chart/defaults'; export const LAYOUTS_KEY = 'layouts'; export const createLocalStorageLayoutProvider = (): LayoutProvider => { const getLayoutsFromLS = (): ChartLayoutData | undefined => { const layoutsFromLS = localStorage.getItem(LAYOUTS_KEY); if (!layoutsFromLS) { return undefined; } return JSON.parse(layoutsFromLS); }; const setLayoutsDataToLS = (data: ChartLayoutData): void => { localStorage.setItem(LAYOUTS_KEY, JSON.stringify(data)); }; return { // this method dxchart recieves array of layouts from your storage getLayouts() { const layoutsData = getLayoutsFromLS(); if (!layoutsData) { console.warn('No layouts created yet'); return Promise.reject(); } return Promise.resolve(layoutsData); }, // this way dxchart notifies that user made some changes to layout // and you should sync its state with your storage updateLayout(layout: ChartLayoutWithId) { const layoutsData = getLayoutsFromLS(); if (!layoutsData) { return Promise.reject(new Error(`There is no localstorage item ${LAYOUTS_KEY}`)); } layoutsData.layouts = replaceInArray( layoutsData.layouts, (item: ChartLayoutWithId) => item.id === layout.id, layout, ); setLayoutsDataToLS(layoutsData); return Promise.resolve(void 0); }, // user can create new layouts for convenience // and this way dxchart notifies about new layouts created // NOTE: if user runs an app for the first time dxchart will create a default layout // and call this method with a default layout provided createLayout(layout: ChartLayoutNamed) { const id = Math.random().toString(36).substring(2, 11); console.log(`Creating new layout: ${id}`); const layoutWithId = { id, ...layout }; const layoutsData = getLayoutsFromLS(); if (!layoutsData) { const lastUpdateTimeStamp = Date.now(); const newLayoutData: ChartLayoutData = { selectedLayoutId: id, layouts: [{ ...layoutWithId, lastUpdateTimeStamp }], theme: DEFAULT_THEME, }; setLayoutsDataToLS(newLayoutData); } else { const lastUpdateTimeStamp = Date.now(); layoutsData.layouts.push({ ...layoutWithId, lastUpdateTimeStamp }); setLayoutsDataToLS(layoutsData); } return Promise.resolve(id); }, // when the user deletes already created layout // dxchart will call this method deleteLayout(id: string) { const layoutsData = getLayoutsFromLS(); if (!layoutsData) { return Promise.resolve(void 0); } layoutsData.layouts = layoutsData.layouts.filter(layout => layout.id !== id); setLayoutsDataToLS(layoutsData); return Promise.resolve(void 0); }, // when the user switches layout we should update the data about // the layout that is currently selected // so when the user reloads the page we can show him // the layout he've seen before updateSelectedLayout(id: string) { const layoutsData = getLayoutsFromLS(); if (!layoutsData) { return Promise.resolve(void 0); } layoutsData.selectedLayoutId = id; setLayoutsDataToLS(layoutsData); return Promise.resolve(void 0); }, // allows to update the whole layout object data // layout data structrure contains layouts array and global variables // the best scenario for this function is to change these global variables // for updating some layout using `updateLayout` function is more preferable updateLayoutData(newLayoutData) { setLayoutsDataToLS(newLayoutData); return Promise.resolve(); }, }; }; ``` --- ## dxcharts-react/Advanced/code/localstorage-userdata-provider.ts ```ts import { UserDataProvider, UserData } from '@dx-private/dxchart5-react/dist/providers/user-data-provider'; export const USER_DATA_KEY = 'userData'; export const createLocalStorageUserDataProvider = (): UserDataProvider => { const getUserDataFromLS = (): UserData | undefined => { const userDataFromLS = localStorage.getItem(USER_DATA_KEY); if (!userDataFromLS) { return undefined; } return JSON.parse(userDataFromLS); }; const setUserDataToLS = (userData: UserData) => { localStorage.setItem(USER_DATA_KEY, JSON.stringify(userData)); }; return { // this method dxcharts will call on init and some other lifecycle events // to get the stored user data getUserData() { const userData = getUserDataFromLS(); if (!userData) { console.warn('No user data created yet'); return Promise.reject(); } return Promise.resolve(userData); }, // when the user changes something in user data this method is called by dxcharts // to notify that you should sync user data state with external store setUserData(newUserData: UserData) { setUserDataToLS(newUserData); return Promise.resolve(void 0); }, }; }; ``` --- ## dxcharts-react/Bundling.mdx import { ChartReactWithDisabledWorkers } from './code/bundling.example.tsx'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration.tsx'; import bundlingCode from '!!raw-loader!./code/bundling.example.tsx'; # Bundling We use [Webpack 5](https://webpack.js.org/) as a bundler internally for our project. This means that we battle tested the bundling process with it and we know that it fully supports all the features `dxchart5-react` has. > We highly recommend to use Webpack 5 as a bundler for your project to integrate `dxchart5-react`, because it's the most stable and feature rich bundler at the moment. ## Different bundlers You may use another bundler for your project but we can't guarantee that `dxchart5-react` integration will work as expected with it. > First of all, please install **the most recent version** of a bundler you like, because they might still be in an active development phase, > and some of the issues might be already fixed in the latest versions. ### Vite As for known issues: Currently we experience an issue with Vite, that relates to usage of webworkers in a dependent packages (such as our `dxchart5-react` for you). You can check out the related threads here [Github issue 1](https://github.com/vitejs/vite/issues/8427) and [Github issue 2](https://github.com/vitejs/vite/issues/11672). Both for a `dev` and `producton` modes we couldn't find a way to make everything work as expected. Therefore, the only solution for now is to disable webworkers in `dxchart5-react`. # Contact Us If you know how to fix any issue with a specific bundler, please contact us, because it will help us a lot to make our product better for everyone. --- ## dxcharts-react/Component Overriding/ChartZoomingTool/chart-zooming-tool-component.mdx import { OverridingChartZoomingToolComponent } from './chart-zooming-tool-component.tsx'; import code from '!!raw-loader!./chart-zooming-tool-component.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import chartZoomingToolCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-zooming-tool/chart-zooming-tool.component'; # Overriding Footer Component You can override default ChartZoomingTool component by providing `ChartZoomingTool` into `uiOverrides` prop to the `ChartReactApp` The ChartZoomingTool component has the following properties: ## Example Chart zooming tool become chart ZOO tool: Source code: {code} --- ## dxcharts-react/Component Overriding/ChartZoomingTool/chart-zooming-tool-component.tsx ```tsx import { ChartReactAppContainer, ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import React, { useCallback, useContext, useEffect, useState } from 'react'; import { OverrideProps, useIcons } from '@dx-private/dxchart5-react/dist/chart/ui-overrides'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { ZoomingToolContainerStyled, ZoomingToolZoomInStyled, ZoomingToolZoomOutStyled } from '@dx-private/dxchart5-react/dist/chart/components/chart-zooming-tool/chart-zooming-tool-container.styled'; import { IconWrapper } from '@dx-private/dxchart5-react/dist/chart-kit/IconWrapper/IconWrapper.component'; import { TEST_IDS } from '@dx-private/dxchart5-react/dist/config/e2e/test-ids'; import { ChartZoomingToolProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-zooming-tool/chart-zooming-tool.component'; import { ChartReactAppContext } from '@dx-private/dxchart5-react/dist/chart/defaults'; import { inRectangle } from '@dx-private/dxchart5-modules/dist/drawings/model/drawing.view-model'; import { MultiChartComponentContext } from '@dx-private/dxchart5-react/dist/chart/components/multi-chart/multi-chart-context'; export const ChartZoomingTool = ({ chartReactAPIProps: _chartApi, originalProps: props }: OverrideProps) => { const { chart, zoomIn, zoomOut, buttonsDisabled, marginBottom, localization, currentCanvasBounds } = props; const [isInZone, setIsInZone] = useState(false); const stopPropagation = useCallback( (e: React.MouseEvent) => { chart.crossEventProducer.fireCrossClose(); e.stopPropagation(); }, [chart.crossEventProducer], ); const { isMobile } = useContext(ChartReactAppContext); useEffect(() => { const unsubMouseMove = chart.canvasInputListener.observeMouseMove().subscribe(point => { const rectX = currentCanvasBounds.width * 0.375; const rectY = currentCanvasBounds.height * 0.85 - marginBottom + 10; const rectWidth = currentCanvasBounds.width * 0.25; const rectHeight = currentCanvasBounds.height * 0.15 + marginBottom + 10; setIsInZone(inRectangle(point, [rectX, rectY, rectWidth, rectHeight])); }); const unsubMouseLeave = chart.canvasInputListener.observeMouseEnter().subscribe(() => { setIsInZone(false); }); return () => { unsubMouseMove.unsubscribe(); unsubMouseLeave.unsubscribe(); }; }, [chart, currentCanvasBounds, marginBottom]); const { zooming } = useIcons(); const { keyboardModeEnabled } = useContext(MultiChartComponentContext); const isVisible = isInZone || isMobile || keyboardModeEnabled; return ( {zooming.zoomin}} testId={TEST_IDS.zoom_button_in} />
{zooming.zoomout}} testId={TEST_IDS.zoom_button_out} />
); }; export const OverridingChartZoomingToolComponent = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((_chartId, _chartInstance) => {}); }; const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
); }; ``` --- ## dxcharts-react/Component Overriding/Footer/footer-component.mdx import { OverridingFooterComponent } from './footer-component.tsx'; import code from '!!raw-loader!./footer-component.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import footerCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-footer/chart-footer.component'; import timeframePresetsCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-timeframe-presets/timeframe-presets.component'; # Overriding Footer Component You can override default footer component and it's elements by providing `ChartFooter` or `TimeframePresets` into `uiOverrides: Footer` prop to the `ChartReactApp` The footer component has the following properties: The footer timeframe component has the following properties: ## Example Footer with removed some default elements and changed styles and timeframe components with removed custom aggregation: Source code: {code} --- ## dxcharts-react/Component Overriding/Footer/footer-component.tsx ```tsx import { ChartReactAppContainer, ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import React, { memo, useCallback, useContext, useMemo, useRef, useState } from 'react'; import { OverrideProps, useIcons } from '@dx-private/dxchart5-react/dist/chart/ui-overrides'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { MultiChartComponentContext } from '@dx-private/dxchart5-react/dist/chart/components/multi-chart/multi-chart-context'; import { useA11yListboxArrowsFocusController } from '@dx-private/dxchart5-react/dist/chart-kit/accessibility/use-a11y-listbox-arrows-focus-controller'; import { TimeframePresetsProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-timeframe-presets/timeframe-presets.component'; import { ChartReactAppContext } from '@dx-private/dxchart5-react/dist/chart/defaults'; import { timeframePresetEq } from '@dx-private/dxchart5-react/dist/chart/model/timeframe-presets.model'; import { ToolbarButtonStyled } from '@dx-private/dxchart5-react/dist/chart/components/chart-toolbar/chart-toolbar-button-with-tooltip.styled'; import { EditButtonActiveIconWrapper, EditButtonIconWrapper, TimeframePresetsContainer, TimeframeScrollableContainer } from '@dx-private/dxchart5-react/dist/chart/components/chart-timeframe-presets/timeframe-presets.styled'; import { TimeframePresetItemDancing } from '@dx-private/dxchart5-react/dist/chart/components/chart-timeframe-presets/timeframe-preset-dancing/timeframe-preset-dancing.component'; import { TimeframePresetItem } from '@dx-private/dxchart5-react/dist/chart/components/chart-timeframe-presets/timeframe-preset/timeframe-preset.component'; import { ChartFooterProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-footer/chart-footer.component'; export const FooterComponent = ({ chartReactAPIProps: _chartApi, originalProps: props }: OverrideProps) => { return (
{props.children}
); }; const TimeframePresetsComponent = memo((overrideProps: OverrideProps) => { const { chartReactAPIProps: _chartApi, originalProps: props } = overrideProps; const { onSelect, presets, onDeletePreset, timeframePreset, } = props; const { config: chartReactConfig } = useContext(ChartReactAppContext); const iconsConfig = useIcons(); const { localization } = useContext(MultiChartComponentContext); const [isEditing, setIsEditing] = useState(false); const [isAdding, setIsAdding] = useState(false); const editPresetsHandler = useCallback(() => { setIsEditing(!isEditing); setIsAdding(false); }, [isEditing]); const containerRef: React.MutableRefObject = useRef(null); const anchorRef: React.MutableRefObject = useRef(null); useA11yListboxArrowsFocusController({ wrapperRef: containerRef, childrenSelector: 'button', direction: 'horizontal', autoFocus: true, deps: [isEditing, presets], anchorRef, }); const highlightedPreset = useMemo( () => timeframePreset ? presets.find(p => timeframePresetEq.equals(p, timeframePreset)) : undefined, [timeframePreset, presets], ); const finishEditPresetsBtn = ( {iconsConfig.layout.tick}} onClick={editPresetsHandler} /> ); const editPresetsBtn = presets.length > 1 && ( {iconsConfig.layout.edit}} onClick={editPresetsHandler} /> ); const editOrFinishEditingPresets = isEditing ? finishEditPresetsBtn : editPresetsBtn; const timeframePresetFull = ( <> {!isAdding && editOrFinishEditingPresets} ); return ( {isEditing ? presets // don't show the last preset, which should always be an "All" preset to prevent removing it .slice(0, -1) .map(preset => ( )) : presets.map(preset => ( {preset.timeframe.label} ))} {chartReactConfig.timeframePresets.mode === 'full' && timeframePresetFull} ); }); export const OverridingFooterComponent = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((_chartId, _chartInstance) => {}); }; const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
null, DrawingGroupsDropdown: () => null, } }} /> ); }; ``` --- ## dxcharts-react/Component Overriding/Icons.mdx --- tags: icons --- import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import CustomIconsCode from '!!raw-loader!./code/Icons.tsx'; import { iconsConfig } from './code/Icons'; import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp'; # Icons ## How to replace You сan replace all icons in `dxchart5-react to your own. To do so you should pass your `IconsConfig` to `uiOverrides` property in `ChartReactApp` component. > `d.ts` for a full list of available icons can be found here: > `import { IconsConfig } from '@dx-private/dxchart5-react/dist/config/icons/icons-config'` Here is an example where we override some icons with `ArrowIcon` just for a demo puprposes: {CustomIconsCode} ### Live Demo ### Quick Hint To understand what icon you need to replace open `Dev Tools` and search for a `data-icon-name` attribute. {``} ## API Reference `d.ts` for the full list of available icons `IconsConfig` is declared here: `@dx-private/dxchart5-react/dist/config/icons/icons-config#IconsConfig`. --- ## dxcharts-react/Component Overriding/Logo.mdx import { ChartReactWithCustomStylesLogo } from './code/Logo.tsx'; import { CustomLogoApp } from './code/CustomLogo/CustomLogoApp.tsx'; import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration.tsx'; import logoCode from '!!raw-loader!./code/Logo.tsx'; import customLogoAppCode from '!!raw-loader!./code/CustomLogo/CustomLogoApp.tsx'; import customLogoComponentCode from '!!raw-loader!./code/CustomLogo/CustomLogo.tsx'; import customLogoComponentStyledCode from '!!raw-loader!./code/CustomLogo/CustomLogoStyled.tsx'; # Logo In `dxchart5-react` you can add your own logo to the chart. The key mechanism to let you provide your logo to the chart is a custom `react` component, therefore you can place it wherever you like in main charts area bounds. ### How to render your logo To provide your logo component to the chart you should use same old `component overriding` concept. Let's dive in through the example. Assume you are software developer at `Apple` and the task is to add `Apple` logo to the chart. Here is our `Logo` component, that we want to see on chart Then you should wire it up via `uiOverrides` property of `ChartReactApp` component. Finally, as a result you will see this ### Options If you want to use our suggested styles for Logo you can pass down `className` that is provided via `props` to your `Logo` component. ### Caveats You can provide any image, any text and anything you want as a `Logo` component, because it's just a `react` component, but here're some recommendations. 1. **src**. If you use some image as a `Logo`, we recommend to use locally imported images, not `URL` to some remote resource. It's better for performance and security reasons. But if you want to, make sure that you have a proper `CORS` policy settings for your image resource. 2. **snapshot**. We provide functionality to make a snapshot of the chart, and `Logo` is a part of a snapshot. The key caveat here, that we use `HTMLCanvas` element as a middleware to convert `DOMElements` to `Image`, therefore there are always some quality issues during rendering and it leads to a flaw, that the `quality` of the image you provided might be not as good as you provided. **We're working on that issue**, but for now, we recommend to try different image sizes, css styles and so on to end up with the quality that would be acceptable for you. ### Custom Logo Logic The logo behavior is fully customizable and up to you, in this section we will add an additional logic by using dxchart5-react API, which allows to move it right to the scales boundaries, so they won't overlap with the logo we passed before: #### Example Here is the example of chart with the described above logic being added: The result should be a custom `left` css property added to the logo, which is calculated based on scales position #### Source code 1. Main component with `ChartReactApp` implemented and `Logo` component being passed as a `uiOverride`: 2. Logo component: 3. Styled component: --- ## dxcharts-react/Component Overriding/Overview.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import Overview from '!!raw-loader!./code/Overview.tsx'; # Component Overrides Component overrides gives you a straightforward way to provide your own components to override the default ones. > Components overrides only works for `React` components. ## Usage In general you need to pass to your `ChartReactApp` component `uiOverrides` prop and then provide your custom components as a value. {Overview} --- ## dxcharts-react/Component Overriding/Settings Tabs/settings-popup-content-tabs.tsx ```tsx import React, { useState, useCallback } from 'react'; import { ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import { overrideDefaultTab } from '@dx-private/dxchart5-react/dist/chart/ui-overrides/settings'; import { Checkbox } from '@dx-private/dxchart5-react/dist/chart-kit/Checkbox/Checkbox.component'; import { ChartSettingsTabForm, ChartSettingsTabGeneralItemStyled, } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings-general/chart-settings-tab-general.styled'; import { ChartSettingsField } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings-field.component'; import { ChartSettingsContentProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings-content.component'; import { chartSettingsLens } from '@dx-private/dxchart5-react/dist/chart/view-models/chart-configurator.view-model'; import { TabTypeWithIcon } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings.model'; import { useIcons } from '@dx-private/dxchart5-react/dist/chart/ui-overrides'; const CustomTradingTab = (props: ChartSettingsContentProps) => { const { value, onValueChange } = props; const [showAlerts, setShowAlerts] = useState(false); const showOrdersAndPositionsHandler = useCallback( (value: boolean = false) => { onValueChange(chartSettingsLens(['chartReact', 'trading', 'visible']), value); onValueChange(chartSettingsLens(['chartReact', 'trading', 'showOrders']), value); onValueChange(chartSettingsLens(['chartReact', 'trading', 'showPositions']), value); }, [onValueChange], ); const showOrdersHandler = useCallback( (value: boolean = false) => { onValueChange(chartSettingsLens(['chartReact', 'trading', 'showOrders']), value); }, [onValueChange], ); const showPositionsHandler = useCallback( (value: boolean = false) => { onValueChange(chartSettingsLens(['chartReact', 'trading', 'showPositions']), value); }, [onValueChange], ); return ( setShowAlerts(!showAlerts)} /> ); }; export const OverriddenTradingTab = () => { const iconsConfig = useIcons(); const myCustomTradingTab: Record = { ChartTradingTab: { id: 'Trading', label: 'Trading', content: () => CustomTradingTab, icon: iconsConfig.drawings.drawingsTypes.callout, }, }; const customDefaultTabTitle = Object.keys(myCustomTradingTab)[0]; const customTab = myCustomTradingTab[customDefaultTabTitle]; const overriddenTabs = overrideDefaultTab(customDefaultTabTitle, customTab); return ( ); }; ``` --- ## dxcharts-react/Component Overriding/Settings Tabs/settings-popup-content.mdx import { OverriddenTradingTab } from './settings-popup-content-tabs.tsx'; import code from '!!raw-loader!./settings-popup-content-tabs.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; ## Changing default tab content You can change default settings tab content by using `overrideDefaultTab` function from the override file ## Example Source code: {code} --- ## dxcharts-react/Component Overriding/Settings Tabs/settings-popup-tabs-component.tsx ```tsx import React from 'react'; import { Checkbox } from '@dx-private/dxchart5-react/dist/chart-kit/Checkbox/Checkbox.component'; import { ChartSettingsContentProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings-content.component'; import { ChartSettingsField } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings-field.component'; import { ChartSettingsFieldset } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings-fieldset.styled'; import { TabTypeWithIcon } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings.model'; import { chartSettingsLens } from '@dx-private/dxchart5-react/dist/chart/view-models/chart-configurator.view-model'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import { ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { addCustomSettingsTab } from '@dx-private/dxchart5-react/dist/chart/ui-overrides/settings'; import { useIcons } from '@dx-private/dxchart5-react/dist/chart/ui-overrides'; const CustomTab = (props: ChartSettingsContentProps) => { return ( { props.onValueChange(chartSettingsLens(['chartCore', 'components', 'crossTool']), { magnetTarget: 'none', type: 'cross-and-labels', }); props.onValueChange(chartSettingsLens(['chartCore', 'components', 'grid', 'visible']), enabled); props.onValueChange(chartSettingsLens(['chartCore', 'components', 'volumes']), { visible: enabled, showSeparately: false, }); }} /> ); }; export const OverridingSettingsTabsComponent = () => { const iconsConfig = useIcons(); const myCustomTabs: TabTypeWithIcon[] = [ { id: 'Custom Tab', label: 'Custom Tab', content: () => CustomTab, icon: iconsConfig.drawings.drawingsTypes.callout, }, ]; const overriddenTabs = addCustomSettingsTab(myCustomTabs, 3); return ( ); }; ``` --- ## dxcharts-react/Component Overriding/Settings-Tabs/settings-popup-tabs.mdx import { OverridingSettingsTabsComponent } from './settings-popup-tabs-component.tsx'; import SettingPopupTabsComponentCode from '!!raw-loader!./settings-popup-tabs-component.tsx'; import TabTypeWithIcon from '!!raw-loader!./settings-popup-tabs.ts'; import ChartSettings from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-settings/chart-settings.model.ts'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; # Overriding Settings Tabs You can add new settings tabs by using `addCustomSettingsTab` function from the override file ## Example Source code: {SettingPopupTabsComponentCode} --- ## dxcharts-react/Component Overriding/Settings Tabs/settings-popup-tabs.ts ```ts import { TabTypeWithIcon } from '@dx-private/dxchart5-react/dist/chart/components/chart-settings/chart-settings.model'; export const TabsContainer = () => { type TabDeclaration = TabTypeWithIcon | string; type ChartSettingsTabs = TabDeclaration[]; const chartSettingsTabs: ChartSettingsTabs = []; return chartSettingsTabs; }; ``` --- ## dxcharts-react/Component Overriding/Sidebar/sidebar-component.mdx import { OverridingSidebarComponent } from './sidebar-component.tsx'; import code from '!!raw-loader!./sidebar-component.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import sidebarCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-sidebar/chart-drawings-sidebar.component'; import sidebarFooterCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-sidebar/chart-drawings-sidebar-footer.component'; # Overriding Sidebar Component You can override default sidebar component by providing `DrawingsSidebarComponent` or `DrawingsSidebarFooter` into `uiOverrides: DrawingsSidebar` prop to the `ChartReactApp` The sidebar component has the following properties: The sidebar footer component has the following properties: ## Example Sidebar changed drawings list and scrollable, Sidebar footer removed hide and sync buttons Source code: {code} --- ## dxcharts-react/Component Overriding/Sidebar/sidebar-component.tsx ```tsx import { ChartReactAppContainer, ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import React, { memo, useCallback, useContext, useRef } from 'react'; import { DrawingSidebarProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/chart-drawings-sidebar.component'; import { OverrideProps, useIcons } from '@dx-private/dxchart5-react/dist/chart/ui-overrides'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { DrawingsSidebarFooter as DefaultDrawingsSidebarFooter, DrawingsSidebarFooterProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/chart-drawings-sidebar-footer.component'; import { DrawingsSidebarHeader } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/chart-drawings-sidebar-header.component'; import { DrawingType, isDrawingType } from '@dx-private/dxchart5-react/dist/chart/model/drawing.model'; import { DrawingsSidebarDrawingIcon } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/components/chart-drawings-sidebar-drawing-icon.component'; import { DrawingsSidebarDrawing } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/components/chart-drawings-sidebar-drawing.component'; import { SidebarSeparatorStyled } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/components/chart-drawings-sidebar-separator.styled'; import { MultiChartComponentContext } from '@dx-private/dxchart5-react/dist/chart/components/multi-chart/multi-chart-context'; import { SidebarFooterButtonType, SidebarFooterButtonTypes } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/chart-sidebar.model'; import { useA11yListboxArrowsFocusController } from '@dx-private/dxchart5-react/dist/chart-kit/accessibility/use-a11y-listbox-arrows-focus-controller'; import { DrawingsSidebarButtonWithTooltip } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/components/chart-drawings-sidebar-button-with-tooltip.component'; import { getSidebarFooterButtonName, getSidebarFooterIconByType } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/footer-functions'; import { DrawingsSidebarFooterStyled } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/chart-drawings-sidebar-footer.styled'; export const DrawingsSidebarComponent = ({ chartReactAPIProps: _chartApi, originalProps: props }: OverrideProps) => { const isActiveDrawing = useCallback( (type: string | number) => (!isDrawingType(props.activeDrawingType) ? false : props.activeDrawingType === type), [props.activeDrawingType], ); return (
{props.drawingGroups.map((group, i) => { return ( {group.drawings.map((type: DrawingType) => { const favorite = props.favoriteDrawings.includes(type); return type === 'icon' ? ( void 0} /> ) : ( void 0} onAddToFavorites={() => void 0} onRemoveFromFavorites={() => void 0} /> ); })} {i === props.drawingGroups.length - 1 ? null : ( )} ); })}
); }; /* remove HIDE and SYNC buttons from sidebar footer */ const sidebarFooterButtonTypes = [ 'MAGNET', 'DRAWING_MODE', // 'SYNC_DRAWINGS', // 'HIDE_DRAWINGS', 'DELETE_DRAWINGS', ] as const; const DrawingsSidebarFooter = memo((overrideProps: OverrideProps) => { const { chartReactAPIProps: _chartApi, originalProps: props } = overrideProps; const { expanded, disabled = false, onButtonClick, buttonsState } = props; const iconsConfig = useIcons(); const { localization } = useContext(MultiChartComponentContext); const footerListRef = useRef(null); const isActive = useCallback( (type: SidebarFooterButtonType) => { switch (type) { case SidebarFooterButtonTypes.DRAWING_MODE: return buttonsState.drawingModeOn; case SidebarFooterButtonTypes.MAGNET: return buttonsState.magnetOn; case SidebarFooterButtonTypes.HIDE_DRAWINGS: return !buttonsState.drawingsVisible; case SidebarFooterButtonTypes.DELETE_DRAWINGS: return false; case SidebarFooterButtonTypes.SYNC_DRAWINGS: return buttonsState.drawingSyncEnabled; } }, [buttonsState], ); useA11yListboxArrowsFocusController({ wrapperRef: footerListRef, childrenSelector: 'li', direction: 'vertical', role: 'listbox', }); const renderSidebarFooterToolbarItem = (type: SidebarFooterButtonType, supressSelect: boolean = false) => { return ( !supressSelect && onButtonClick(type)} disableTooltip={expanded} disabled={disabled} isActive={isActive(type)} /> ); }; return ( <> {sidebarFooterButtonTypes.map(type => renderSidebarFooterToolbarItem(type))} ); }); export const OverridingSidebarComponent = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((_chartId, _chartInstance) => {}); }; const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
); }; ``` --- ## dxcharts-react/Component Overriding/Snapshot sharing menu/snapshot-options.mdx import { OverridingSnapshotOptions } from './snapshot-options.tsx'; import code from '!!raw-loader!./snapshot-options.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import snapshotDropdownCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-snapshot/chart-snapshot-dropdown.component.tsx'; import snapshotOverrideCode from '!!raw-loader!../../../../../../chart-react/src/chart/ui-overrides/snapshot-menu-items'; # Overriding snapshot sharing options You can override default snapshot menu options by providing `SnapshotMenuItems` into `uiOverrides` prop to the `ChartReactApp` The list of default snapshot items: = ['} endDeclarationStr={'\n]'} /> ## Example Source code: {code} --- ## dxcharts-react/Component Overriding/Snapshot sharing menu/snapshot-options.tsx ```tsx import React from 'react'; import { DrawingsSidebarFooterStyled } from '@dx-private/dxchart5-react/dist/chart/components/chart-sidebar/chart-drawings-sidebar-footer.styled'; import { ChartMainAreaStyled } from '@dx-private/dxchart5-react/dist/chart/components/multi-chart/multi-chart.styled'; import { createGlobalStyle } from 'styled-components'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import { ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; const GlobalStyles = createGlobalStyle` ${ChartMainAreaStyled} { height: calc(100% + 44px); } ${DrawingsSidebarFooterStyled} { position: static; } `; export const OverridingSnapshotOptions = () => { const overridenSnapshotItems = ['downloadImage', 'copyImage', 'copyLink']; return ( <> {/* @ts-ignore types are not compatible with react 18 */} ); }; ``` --- ## dxcharts-react/Component Overriding/Toolbar/toolbar-component.mdx import { OverridingToolbarComponent } from './toolbar-component.tsx'; import code from '!!raw-loader!./toolbar-component.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import toolbarCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/chart-toolbar/chart-toolbar.component'; # Overriding Toolbar Component You can override default toolbar component by providing `Toolbar` into `uiOverrides` prop to the `ChartReactApp` The toolbar component has the following properties: ## Example Source code: {code} --- ## dxcharts-react/Component Overriding/Toolbar/toolbar-component.tsx ```tsx import { ChartReactAppContainer, ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import React, { useEffect, useState } from 'react'; import { ChartToolbarProps } from '@dx-private/dxchart5-react/dist/chart/components/chart-toolbar/chart-toolbar.component'; import { DEFAULT_TOOLBAR_BUTTONS } from '@dx-private/dxchart5-react/dist/chart/ui-overrides/toolbar'; const CustomToolbar = (props: ChartToolbarProps) => { const { MainInstrumentComponent, buttons } = props; return (

Custom Toolbar

{MainInstrumentComponent && }
{buttons}
); }; export const OverridingToolbarComponent = () => { const [Toolbar, setToolbar] = useState({ ToolbarContainer: null, ToolbarButtons: DEFAULT_TOOLBAR_BUTTONS, ToolbarComponent: CustomToolbar, }); useEffect( () => setToolbar({ ToolbarContainer: null, ToolbarButtons: DEFAULT_TOOLBAR_BUTTONS, ToolbarComponent: CustomToolbar, }), [], ); const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
); }; ``` --- ## dxcharts-react/Component Overriding/Toolbar Buttons/toolbar-buttons.mdx import { OverridingToolbarButtons } from './toolbar-buttons.tsx'; import code from '!!raw-loader!./toolbar-buttons.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import toolbarButtonsCode from '!!raw-loader!../../../../../../chart-react/src/chart/ui-overrides/toolbar'; # Overriding Toolbar Buttons You can override default toolbar buttons by providing `Toolbar` into `uiOverrides` prop to the `ChartReactApp` To add custom buttons you can use `addCustomToolbarButtons` function which accepts an array of buttons You can also render toolbar inside your element, in order to do so you should provide `ToolbarContainer` ## Example Source code: {code} --- ## dxcharts-react/Component Overriding/Toolbar Buttons/toolbar-buttons.tsx ```tsx import { ChartReactAppContainer, ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import React, { memo, useEffect, useRef, useState } from 'react'; import { Button } from '@dx-private/dxchart5-react/dist/chart-kit/Button/Button.component'; import { ChartToolbar } from '@dx-private/dxchart5-react/dist/chart/components/chart-toolbar/chart-toolbar.component'; import { ChartReactAPIProps } from '@dx-private/dxchart5-react/dist/chart/ui-overrides/index'; import { DEFAULT_TOOLBAR_BUTTONS, addCustomToolbarButtons, } from '@dx-private/dxchart5-react/dist/chart/ui-overrides/toolbar'; const leftAlignButton = memo(props => { return ; }); const rightAlignButton = memo(props => { return ; }); const toolbarButtons = addCustomToolbarButtons({ button: rightAlignButton }, { button: leftAlignButton }); export const OverridingToolbarButtons = () => { const toolbarPlace = useRef(null); const [Toolbar, setToolbar] = useState({ ToolbarContainer: toolbarPlace.current, ToolbarButtons: DEFAULT_TOOLBAR_BUTTONS, ToolbarComponent: ChartToolbar, }); useEffect( () => setToolbar({ ToolbarContainer: toolbarPlace.current, ToolbarButtons: toolbarButtons, ToolbarComponent: ChartToolbar, }), [], ); const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
Some text
); }; ``` --- ## dxcharts-react/Component Overriding/Trading/order-component.mdx import code from '!!raw-loader!./order-component.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import regularOrderCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/trading/order/regular-order.component'; import orderComponentWithoutClose from './img/trading_order_component.jpg'; # Overriding Order Component You can override default Order component by providing `RegularOrder` into `uiOverrides: trading` prop to the `ChartReactApp` The Order component without Close button icon has the following properties: ```typescript jsx const OrderComponent = (props: OrderProps) => { const { children, absoluteChildren, side, selected = false, disabled = false, className, testId, onClick, onSelect, onDblClick, onDeselect, withDeselectBtn = false, data = {}, canCreateOCO = false, onCreateOcoOrders, } = props; /* your code */ return ( {withDeselectBtn && deselectBtnTransition( (styles, selected) => selected && ( ), )} {absoluteChildren} {children} {canCreateOCO && ocoBtnTransition( (styles, selected) => selected && ( ), )} {/* withCloseBtn && removed close Button */} ); }; ``` ```typescript jsx export const OverridingOrderComponent = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((_chartId, _chartInstance) => {}); }; const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
); }; ``` Order component without close button Source code: {code} --- ## dxcharts-react/Component Overriding/Trading/order-component.tsx ```tsx import React, { MouseEvent, useCallback, useContext, useMemo, useRef } from 'react'; import { ChartReactAppContainer, ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import { OverrideProps } from '@dx-private/dxchart5-react/dist/chart/ui-overrides'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { OrderProps } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/components/order.component'; import { animated, useTransition } from '@react-spring/web'; import { CreateOcoOrdersButton, useOcoButtonsTransition, } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/components/create-oco-orders-button.component'; import { getRegularOrderName, isSelectSkippable, renderSLTPShortcutSection, skipSelectOrder, } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/order.functions'; import { OrderContainerInnerStyled, OrderContainerStyled, OrderDelimiterStyled, OrderSectionStyled, } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/components/order.styled'; import { DeselectOrderButton } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/components/deselect-order-button.component'; import { Side } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/components/side.component'; import { RegularOrderProps } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/regular-order.component'; import { checkOrderIsOnUIOnly, getOrderPriceByType, isProtection, } from '@dx-private/dxchart5-react/dist/chart/model/trading/trading.model'; import { AddSLOrderBtnStyled, AddTPOrderBtnStyled, OrderSLTPShortcutSectionStyled, } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/regular-order.styled'; import { TEST_IDS } from '@dx-private/dxchart5-react/dist/config/e2e/test-ids'; import { defaultOrderPriceFormatter } from '@dx-private/dxchart5-react/dist/chart/model/trading/order.model'; import { OrderLineStyled } from '@dx-private/dxchart5-react/dist/chart/components/trading/order/components/side.styled'; import { MultiChartComponentContext } from '@dx-private/dxchart5-react/dist/chart/components/multi-chart/multi-chart-context'; const OrderComponent = (props: OrderProps) => { const { children, absoluteChildren, side, selected = false, disabled = false, className, testId, onClick, onSelect, onDblClick, onDeselect, withDeselectBtn = false, data = {}, canCreateOCO = false, onCreateOcoOrders, } = props; const timeoutId: React.MutableRefObject = useRef(null); const deselectBtnTransition = useTransition(selected && withDeselectBtn, { config: { duration: 150, }, from: { position: 'absolute' as const, y: 0, x: 0, zIndex: -1, opacity: 0 }, enter: { x: -20, opacity: 1 }, leave: { x: 0, opacity: 0 }, delay: 150, }); const ocoBtnTransition = useOcoButtonsTransition(selected && canCreateOCO); const onDeselectHandler = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); if (onDeselect) { onDeselect(); } }, [onDeselect], ); const onClickHandler = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); timeoutId?.current && clearTimeout(timeoutId.current); if (onClick && e.detail === 1) { timeoutId.current = setTimeout(onClick, 200); } else if (onDblClick && e.detail === 2) { onDblClick(); } if (onSelect && !isSelectSkippable(e)) { onSelect(); } else { skipSelectOrder(e, false); } }, [onSelect, onClick, onDblClick], ); const dataAttrs = useMemo( () => Object.entries(data).reduce((acc, [k, v]) => ({ ...acc, [`data-${k}`]: v }), {}), [data], ); return ( {withDeselectBtn && deselectBtnTransition( (styles, selected) => selected && ( ), )} {absoluteChildren} {children} {canCreateOCO && ocoBtnTransition( (styles, selected) => selected && ( ), )} {/* withCloseBtn && removed close Button */} ); }; export const RegularOrder = ({ chartReactAPIProps: _chartApi, originalProps: props, }: OverrideProps) => { const { order, createProtectionOrder, takeProfitStopLossEnabled, showPriceAsLabels, onClose, onDeselect, onSelect, onClick, onDblClick, isLineVisible = true, horizontalLineWidth, onCreateOcoOrders, } = props; const { localization } = useContext(MultiChartComponentContext); const { disabled, selected, model, marketPrice } = order; const { id, side, quantity, orderType, limitPrice, stopPrice } = model; const [isSLLinked, isTPLinked] = !isProtection(order.model) && order.model.protectionOrderIds ? order.model.protectionOrderIds.map(id => !!id && !checkOrderIsOnUIOnly(id)) : [false, false]; const [showSLBtn, showTPBtn] = !isProtection(order.model) && order.model.protectionOrderIds ? order.model.protectionOrderIds.map(id => Boolean(id)) : [false, false]; const onSelectHandler = useCallback(() => onSelect && !selected && onSelect(id), [id, onSelect, selected]); const onClickHandler = useCallback(() => onClick && onClick(id), [id, onClick]); const onDblClickHandler = useCallback(() => onDblClick && onDblClick(id), [id, onDblClick]); const onDeselectHandler = useCallback(() => onDeselect && onDeselect(id), [id, onDeselect]); const onCloseHandler = useCallback(() => onClose && onClose(id), [id, onClose]); const addTPOrderHandler = useCallback( (e: MouseEvent) => { e.stopPropagation(); createProtectionOrder && createProtectionOrder('tp', id); }, [createProtectionOrder, id], ); const addSLOrderHandler = useCallback( (e: MouseEvent) => { e.stopPropagation(); createProtectionOrder && createProtectionOrder('sl', id); }, [createProtectionOrder, id], ); const tpTransition = useTransition(selected && !showTPBtn, { config: { duration: 150, }, from: { position: 'absolute' as const, y: 0, top: 0, left: '50%', zIndex: -1, x: '-50%', opacity: 0 }, enter: { y: side === 'buy' ? -20 : 20, opacity: 1, }, leave: showTPBtn ? { opacity: 0 } : { y: 0, opacity: 0 }, }); const slTransition = useTransition(selected && !showSLBtn, { config: { duration: 150, }, from: { position: 'absolute' as const, y: 0, top: 0, left: '50%', zIndex: -1, x: '-50%', opacity: 0 }, enter: { y: side === 'buy' ? 20 : -20, opacity: 1, }, leave: showSLBtn ? { opacity: 0 } : { y: 0, opacity: 0 }, }); const renderSLTPButtons = useMemo(() => { return ( <> {tpTransition( (styles, show) => show && ( {localization.trading.protectionOrders.addButtons.takeProfit} ), )} {slTransition( (styles, show) => show && ( {localization.trading.protectionOrders.addButtons.stopLoss} ), )} ); }, [ tpTransition, slTransition, addTPOrderHandler, addSLOrderHandler, localization.trading.protectionOrders.addButtons.stopLoss, localization.trading.protectionOrders.addButtons.takeProfit, ]); const price = getOrderPriceByType(orderType, limitPrice, stopPrice, marketPrice); return ( <> {quantity} {`${getRegularOrderName(orderType, localization.trading)} ${ !showPriceAsLabels ? (order.formatter || defaultOrderPriceFormatter)(price) : '' }`} {(isSLLinked || isTPLinked) && ( <> {renderSLTPShortcutSection(isSLLinked, isTPLinked)} )} {isLineVisible && ( )} ); }; export const OverridingOrderComponent = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((_chartId, _chartInstance) => {}); }; const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
); }; ``` --- ## dxcharts-react/Component Overriding/Trading/order-entry-component.mdx import { OverridingOrderEntryComponent } from './order-entry-component.tsx'; import code from '!!raw-loader!./order-entry-component.tsx'; import { MDXCodeBlock } from '../../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import orderEntryCode from '!!raw-loader!../../../../../../chart-react/src/chart/components/trading/order-entry/order-entry.component'; # Overriding Order Entry Component You can override default Order Entry component by providing `OrderEntry` into `uiOverrides: trading` prop to the `ChartReactApp` The Order Entry component has the following properties: ## Example Order Entry without counter controls + and -: Source code: {code} --- ## dxcharts-react/Component Overriding/Trading/order-entry-component.tsx ```tsx import React, { memo, useCallback, useEffect, useState } from 'react'; import { ChartReactAppContainer, ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { FlexContainer } from '../../../../components/ui/FlexContainer'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { OrderSide, OrderType } from '@dx-private/dxchart5-react/dist/chart/model/trading/order.model'; import { RootClose } from '@dx-private/dxchart5-react/dist/chart-kit/RootClose/RootClose'; import { OrderEntryBtnContainerStyled, OrderEntryContainerStyled, } from '@dx-private/dxchart5-react/dist/chart/components/trading/order-entry/order-entry.styled'; import { getDecimalRest, OrderEntryInputProps, } from '@dx-private/dxchart5-react/dist/chart/components/trading/order-entry/order-entry-input.component'; import { OrderEntryAddButton } from '@dx-private/dxchart5-react/dist/chart/components/trading/order-entry/order-entry-add-button.component'; import { OrderEntryProps } from '@dx-private/dxchart5-react/dist/chart/components/trading/order-entry/order-entry.component'; import { OverrideProps } from '@dx-private/dxchart5-react/dist/chart/ui-overrides'; import { TEST_IDS } from '@dx-private/dxchart5-react/dist/config/e2e/test-ids'; import { MAIN_FONT } from '@devexperts/dxcharts-lite/dist/chart/chart.config'; import { getTextWidth } from '@dx-private/dxchart5-react/dist/utils/script-title.utils'; import { OrderEntryInputContainerStyled, OrderEntryLeftBuy, OrderEntryLeftSell, OrderEntryQuantityInputStyled, OrderEntryRightBuy, OrderEntryRightSell, OrderQuantityComponentStyled, } from '@dx-private/dxchart5-react/dist/chart/components/trading/order-entry/order-entry-input.styled'; const PLACEHOLDER = '0'; const BUY_BUTTON_TEST_ID = TEST_IDS.order_entry_buy_button; const SELL_BUTTON_TEST_ID = TEST_IDS.order_entry_sell_button; const buildOrderEntryRegexp = (precision: number) => { const integerOnly = precision === 0; // pattern for rest part .12345 const zeroDecimalRest = `\\.[0-9]{0,${precision}}$`; const aboveZeroDecimalRest = `\\.?[0-9]{0,${precision}}$`; // pattern for 0...1 numbers const zeroPattern = `^[0]${integerOnly ? '$' : zeroDecimalRest}`; // pattern for numbers above 1 const aboveZeroPattern = `^[1-9][0-9]*${integerOnly ? '$' : aboveZeroDecimalRest}`; return new RegExp(`(${aboveZeroPattern})|(${zeroPattern})`); }; export const OrderEntryInput = memo(props => { const { type, disabled, quantity, quantityPrecision, maxQuantity, onQuantityChange, createOrder, className, tradingDict, } = props; // we need state to control value inside component, because there is a case when user type "100.", // and this value should be visible for user, but VM stores 100 const [inputValue, setInputValue] = useState(''); // if quanity is undefined, set input empty useEffect(() => setInputValue(quantity === undefined ? '' : String(quantity)), [quantity]); const validateStringOE = useCallback( (value: string, limit: number) => { if (limit < Number(value)) { return false; } if (buildOrderEntryRegexp(quantityPrecision).test(value)) { return true; } return value === '' || value === '0'; }, [quantityPrecision], ); const quantityChangeHandler = useCallback( (newQuantity: string) => { if (validateStringOE(newQuantity, maxQuantity)) { setInputValue(newQuantity); // if input is empty, set null for quantity onQuantityChange(newQuantity === '' ? undefined : Number(newQuantity)); } }, [onQuantityChange, maxQuantity, validateStringOE], ); // adjust input value if precision is changed by chart-react-api const inputValueWithPrecision = quantity !== undefined && getDecimalRest(quantity)?.length > quantityPrecision ? `${quantity.toFixed(quantityPrecision)}` : inputValue; const onBuyLimit = useCallback(() => createOrder('limit', 'buy'), [createOrder]); const onSellLimit = useCallback(() => createOrder('limit', 'sell'), [createOrder]); const onBuyStop = useCallback(() => createOrder('stop', 'buy'), [createOrder]); const onSellStop = useCallback(() => createOrder('stop', 'sell'), [createOrder]); const onBuyMarket = useCallback(() => createOrder('market', 'buy'), [createOrder]); const onSellMarket = useCallback(() => createOrder('market', 'sell'), [createOrder]); const FONT = `12px ${MAIN_FONT}`; const quantityWidth = getTextWidth(inputValueWithPrecision || PLACEHOLDER, FONT) + 40; return ( {type === 'BuyLimitSellStop' && ( {tradingDict.orderEntry.buyLimitBtn} )} {type === 'SellLimitBuyStop' && ( {tradingDict.orderEntry.sellLimitBtn} )} {type === 'BuyMarketSellMarket' && ( {tradingDict.orderEntry.buyBtn} )} '
{type === 'SellLimitBuyStop' && ( {tradingDict.orderEntry.buyStopBtn} )} {type === 'BuyLimitSellStop' && ( {tradingDict.orderEntry.sellStopBtn} )} {type === 'BuyMarketSellMarket' && ( {tradingDict.orderEntry.sellBtn} )}
); }); const ANIMATION_TIME = 400; const OrderEntry = memo>( ({ chartReactAPIProps: _chartReactAPIProps, originalProps: props }) => { const { opened, disabled, setOpened, resetToDefault, validate, createOrder, type, quantity, quantityPrecision, quantityStep, maxQuantity, onChangeQuantity, className, tradingDict, padding, yAxisAlign, domMutationProps, } = props; const [oeBtnVisible, setOeBtnVisible] = useState(true); const startEditingHandler = useCallback(() => setOpened(true), [setOpened]); const onCloseHandler = useCallback(() => { setOpened(false); resetToDefault(); }, [setOpened, resetToDefault]); const createOrderHandler = useCallback( (type: OrderType, side: OrderSide) => { if (validate && !validate()) { return; } createOrder(type, side); onCloseHandler(); }, [createOrder, validate, onCloseHandler], ); useEffect(() => { // -100 is needed because OE button should appears earlier than input disappears opened ? setOeBtnVisible(false) : setTimeout(() => setOeBtnVisible(true), ANIMATION_TIME - 100); }, [opened]); return (
); }, ); export const OverridingOrderEntryComponent = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((_chartId, _chartInstance) => {}); }; const chartReactAppContainerProps = { width: 800, height: 50 }; return ( <>
); }; ``` --- ## dxcharts-react/Component Overriding/code/CustomLogo/CustomLogo.tsx ```tsx import React, { memo, useLayoutEffect, useState } from 'react'; import { MultiChartLayoutType } from '@dx-private/dxchart5-react/dist/chart/model/multichart.model'; import appleLogo from '../apple-small.png'; import { DemoLogoStyled } from './CustomLogoStyled'; /** * logo is attached to the bottom-left corner, * thus changing multi chart layout or scales should give different corresponding chart id to get Y axes later on */ export const getLogoChartId = (layout: MultiChartLayoutType): string => { switch (layout) { case '1x1': case '1x2': case '1x3': return '0'; case '2x1': return '1'; case '3x1': return '2'; case '2x2': return '2'; case '2x4': return '4'; default: return '0'; } }; interface DemoLogoProps { readonly offsetCBRef: React.MutableRefObject<(value: number) => void>; } export const CustomLogo = memo((props: DemoLogoProps) => { const { offsetCBRef } = props; const [offset, setOffset] = useState(0); useLayoutEffect(() => { offsetCBRef.current = setOffset; }, [offsetCBRef]); return ; }); ``` --- ## dxcharts-react/Component Overriding/code/CustomLogo/CustomLogoApp.tsx ```tsx import React, { memo, useCallback, useRef } from 'react'; import { CustomLogo, getLogoChartId } from './CustomLogo'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; import { option } from 'fp-ts'; import { pipe } from 'fp-ts/function'; import { ChartReactAppWrapper } from '../../../../../components/utils/ChartReactApp'; export const CustomLogoApp = memo(() => { // we add a ref here, since offset as a state would lead to component rerender, which also affects ChartReactApp const offsetCBRef = useRef<(value: number) => void>(() => void 0); const setOffset = useCallback((value: number) => offsetCBRef.current(value), []); // this function allows to calculate logo left offset to place it to the right of Y scales const recalculateLogoPosition = useCallback( (api: ChartReactAPI) => { const multiChartLayout = api.getMultiChartLayout(); const chartId = getLogoChartId(multiChartLayout); const chart = api.getChartInfo(chartId).chart; const offset = chart ? chart?.bounds.yAxisBoundsContainer.getYAxisWidths().left.reduce((sum, width) => sum + width, 0) : 0; setOffset(offset); }, [setOffset], ); // add additional logic to onApiCreated with API supported callbacks const onApiCreatedLogo = useCallback( (api: ChartReactAPI) => { // recalculate logo position on initial load and if multichart layout changed, maximized chart or moved any scale // documentation app has no layout provider implemented so it won't be used here, but in the real application it should setTimeout(() => recalculateLogoPosition(api), 1500); api.onMultiChartLayoutChanged(() => recalculateLogoPosition(api)); api.onMaximizedChartIdChanged(() => { const maximizedChartId = api.getMaximizedChartId(); pipe( maximizedChartId, option.fold( () => recalculateLogoPosition(api), id => { const chart = api.getChartInfo(id).chart; const offset = chart ? chart?.bounds.yAxisBoundsContainer .getYAxisWidths() .left.reduce((sum, width) => sum + width, 0) : 0; setOffset(offset); }, ), ); }); api.onYAxisAlignMoved(() => recalculateLogoPosition(api)); api.onStudiesScalesExtentChanged(() => recalculateLogoPosition(api)); }, [recalculateLogoPosition, setOffset], ); return ( }} /> ); }); ``` --- ## dxcharts-react/Component Overriding/code/CustomLogo/CustomLogoStyled.tsx ```tsx // styled components logic import styled from 'styled-components'; const INITIAL_HORIZONTAL_OFFSET = 10; export const DemoLogoStyled = styled.img` position: absolute; left: ${props => props.offset + INITIAL_HORIZONTAL_OFFSET}px; bottom: 40px; width: 30px; height: 30px; `; interface DemoLogoStyledProps { offset: number; } ``` --- ## dxcharts-react/Component Overriding/code/Icons.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; const arrowIcon = ( ); export const iconsConfig = { selectBox: { arrow: arrowIcon, }, chartTypes: { candle: arrowIcon, }, toolbar: { chartSettings: arrowIcon, }, }; export const ChartReactAppWithCustomIcons = () => { return ; }; ``` --- ## dxcharts-react/Component Overriding/code/Logo.tsx ```tsx import React, { memo } from 'react'; import styled from 'styled-components'; import appleLogo from './apple-small.png'; import { ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { LogoProps } from '@dx-private/dxchart5-react/dist/chart/ui-overrides/logo/Logo'; const LogoImageStyled = styled.img` position: absolute; left: 30px; bottom: 40px; width: 30px; height: 30px; `; export const LogoWithCustomStyles = memo(() => { // appleLogo is a locally imported Apple inc. logo return ; }); export const LogoWithDefaultStyles = memo(({ className }) => { // appleLogo is a locally imported Apple inc. logo // className includes suggested styles that are provided by `dxchart5-react` return logo; }); export const ChartReactWithDefaultStylesLogo = () => { return ; }; export const ChartReactWithCustomStylesLogo = () => { return ; }; ``` --- ## dxcharts-react/Component Overriding/code/Overview.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const YourApp = () => { return ( ); }; ``` --- ## dxcharts-react/Date Format.mdx --- tags: chart-react,config --- import DateFormatOverride from '!!raw-loader!./code/date-format-override.tsx'; import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import { RenderDedokTable } from '../../components/dedok/DedokTable/DedokTable'; import DateFormat from './img/DateFormat.png'; # Date Format Sometimes you want different date & time formats than the default ones. You can override the default formats by providing a custom formatters using `chartReactConfig`. ### Example
## Custom date format By default `dxcharts-lite` uses default `DateTimeFormatter` while working with dates. But be careful, this formatter is used not only to format XAxis labels but in a whole `dxcharts-lite` lib. You can override default formatter by providing your own implementation: After overriding default `DateTimeFormatter`, you should see new labels format: {' '} --- ## dxcharts-react/Drawings/Drawings Configuration.mdx import ChartReactConfig from '!!raw-loader!../../../../../chart-react/src/config/chart-react-config.ts'; import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp.tsx'; import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration'; # Drawings Configuration This document provides an overview of configuration options for drawings in `ChartReactConfig`. Below, you will find detailed instructions to configure drawings for your application. ## Config Overview Configuration for drawings can be found in `ChartReactConfig`. ## Sidebar or Toolbar You can determine the visibility of the drawing tools in the sidebar or the toolbar by passing a flag. By default sidebar drawings are enabled, and toolbar drawings disabled. For example, you pass `enabled: false` in config for sidebar, and you will see chart without drawings: If you want to have a list of drawings in toolbar, please pass `enabled: false` for sidebar and pass `enabled: true` for toolbar in config: ## List of Drawings Control the list of drawings available in your application by setting the `drawingsList` value. Provide an array of drawing names to create a simple list of drawings. To create a custom list of drawings, pass your custom drawings list: ```typescript drawingsList: [ { groupName: 'Your group name 1', drawings: ['line', 'extended_line'], }, { groupName: 'Your group name 2', drawings: ['rectangle', 'arc'], } ] ``` This will display a chart with two groups containing four drawings. ## Limit of Drawings You can configure the maximum number of drawings that can be added to the chart. If the limit is reached, a notification will appear at the top of the chart. ## Drawing Groups Enable an input for creating custom drawing groups in the configuration. ```typescript drawingGroups: { enabled: true } ``` With this setting, a selectbox will appear at the bottom-right of the chart, allowing you to create new drawing groups. ## Further reading - [Drawings module](dxcharts-lite/Drawings-Module/Overview) - [Drawings dxchart5-react](dxcharts-react/Drawings/Overview) - [Custom drawing creation](dxcharts-lite/Drawings-Module/Custom-Drawing) --- ## dxcharts-react/Drawings/Overview.mdx import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import code from '!!raw-loader!./code/DrawingsExample'; import { ChartWithDrawingsFromExternalStorage } from './code/DrawingsExample'; # Drawings in dxchart5-react > It is recommended to read [drawings module](dxcharts-lite/Drawings-Module/Overview) article first, because all core functionality is described there. > **Drawings in dxchart5-react are already wired up automatically**, there is no need to add them as a module ## How to access DrawingsComponent from dxchart5-react. Each chart instance has it's own `DrawingsComponent`, which should be located in `chart.drawings`. To get chart instances simply store it into some variable in `api.onChartCreated` function: If [multichart](dxcharts-react/Multichart) option is enabled, each `DrawingsComponent` will manage the corresponding chart instance drawings. Chart instances are arranged from left to right first and then from top to bottom ## Set drawings from external storage Chart drawings module has `setDrawings` API which could be useful to set drawings which were taken from some external storage, for example, local storage or a database. Let's set some drawings right after chart load is completed and apply it to chart. > If you use layout as a way to storage drawings [layout provider](dxcharts-react/Advanced/Sync-state-with-external-storage) should be implemented, without it drawings wouldn't be saved. After that, drawings could be applied to chart. ## Example ## Further reading - [Drawings module](dxcharts-lite/Drawings-Module/Overview) - [Drawings configuration](dxcharts-react/Drawings/Drawings-Configuration) - [Custom drawing creation](dxcharts-lite/Drawings-Module/Custom-Drawing)./code/DrawingsExample --- ## dxcharts-react/Drawings/code/DrawingsExample.tsx ```tsx import React from 'react'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { Drawing } from '@dx-private/dxchart5-modules/dist/drawings/drawings.config'; import { DrawingModel } from '@dx-private/dxchart5-modules/dist/drawings/model/drawing.model'; import { DrawingType } from '@dx-private/dxchart5-react/dist/chart/model/drawing.model'; import { DEFAULT_DRAWINGS_CONFIG } from '@dx-private/dxchart5-react/dist/config/drawings-config'; import { uuid } from '@dx/codex-core/src/utils/id-generator.utils'; import { generateCandles } from '@dx-private/dxchart5-react/dist/utils/generator/candle-generator.utils'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; export const ChartApp = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((chartId, chartInstance) => { // here you access DrawingsComponent in each chart // this callback will be called as many times as many charts in multichart layout you have const drawingsComponent = chartInstance.drawings; }); }; return (
); }; const generateDrawings = (): Record[]> => { const candles = generateCandles(); const symbol = 'AAPL'; const instrumentDrawings: Drawing[] = []; const drawings: Record[]> = { [symbol]: instrumentDrawings }; const firstDrawingCandle = candles[candles.length - 70]; const secondDrawingCandle = candles[candles.length - 1]; const drawingType = 'gann_box'; const points = [ { timestamp: firstDrawingCandle.timestamp, value: firstDrawingCandle.close + firstDrawingCandle.close * 0.15, }, { timestamp: secondDrawingCandle.timestamp + Math.pow(10, 3), value: secondDrawingCandle.close + secondDrawingCandle.close * 0.05, }, ]; const figure: DrawingModel = new DrawingModel( `${drawingType}_${uuid()}`, Date.now() + Math.random(), drawingType, points, DEFAULT_DRAWINGS_CONFIG[drawingType].properties, ); instrumentDrawings.push({ ...figure, _internalDrawing: figure }); return drawings; }; const DRAWINGS_FROM_EXTERNAL_STORAGE: Record[]> = generateDrawings(); export const ChartWithDrawingsFromExternalStorage = () => { const onApiCreated = (api: ChartReactAPI) => { // set drawings to each chart api.onChartCreated((chartId, chartInstance) => { // drawings could be taken from anywhere, for example, localstorage or database if (chartId === '0') { chartInstance.drawings.setDrawings(DRAWINGS_FROM_EXTERNAL_STORAGE); } }); }; return (
); }; ``` --- ## dxcharts-react/Extended Hours.mdx import extendedHours from './img/extendedHours.png'; import sessionBreaks from './img/sessionBreaks.png'; # Extended Hours Extended hours is a part of [ChartReactConfig](dxcharts-react/api-reference-configuration) and also a `dxcharts-react` setting, which allows to configure which sessions data should be displayed on chart. > Important note: both [chartDataProvider](dxcharts-react/Quick-Start---Providers) and [TradingSessionsProvider](dxcharts-react/API-Reference---Providers) should be implemented to support this feature completely. [chartDataProvider](dxcharts-react/Quick-Start/Quick-Start---Providers) is mandatory since dxcharts-react expects to receive additional candles to display them. [TradingSessionsProvider](dxcharts-react/API-Reference---Providers) is optional to highlight the pre and post market sessions candles which [chartDataProvider](dxcharts-react/Quick-Start/Quick-Start---Providers) generated before When `extendedHours` property is `enabled` two things happen: - [chartDataProvider](dxcharts-react/Quick-Start---Providers) method `requestHistoryData` will be called with `extendedHours` option `true` value: ```ts chartDataProvider.requestHistoryData('AAPL', '1h', { extendedHours: true }). ``` In return `dxcharts-react` will expect candles from all trading sessions: **'REGULAR'**, **PRE_MARKET'**, **'POST_MARKET'** and **NO_TRADING** and redraw chart after the data is received with all trading sessions data. - [TradingSessionsProvider](dxcharts-react/API-Reference---Providers) allows to differentiate which candles data belongs to which trading session, it's method `generateSessions` takes `from` and `to` timestamps and also instrument's name and it's trading hours. After that it generates corresponding to this time sessions, which are used to highlight pre and post market sessions candles: ```ts tradingSessionsProvider.generateSessions(1718913600000, 1727870571446, { symbol: 'AAPL', tradingHours: 'NewYorkETH()' }), ``` As the result additional trading sessions candles are displayed and highlighed on the chart: ## Session Breaks > [TradingSessionsProvider](dxcharts-react/API-Reference---Providers) should be implemented to support this feature When `sessionBreaks` is `enabled` vertical lines appear on chart that indicate the start and the end of different trading sessions. To know where to draw a line, dxcharts-react receives trading sessions schedule via `tradingSessionsProvider.generateSessions` the same way extended hours do: ## Further reading - [dxcharts-lite session breaks](dxcharts-lite/Session-Breaks) - [ChartDataProvider](dxcharts-react/Quick-Start---Providers) - [TradingSessionsProvider](dxcharts-react/API-Reference---Providers) - [ChartReactConfig](dxcharts-react/api-reference-configuration) --- ## dxcharts-react/Fonts.mdx # How to configure fonts in `dxchart5-react`? By following the instructions below, you can easily replace the default fonts with your own. ## Default Fonts By default, `dxchart5-react` application uses the following fonts: ```css --font-main: 'Open Sans', sans-serif; --font-main-semibold: 'Open Sans Semibold', sans-serif; --font-main-bold: 'Open Sans Bold', sans-serif; --font-editor: 'IBM Plex Mono', monospace; --font-editor-semibold: 'IBM Plex Mono SemiBold', monospace; --font-editor-bold: 'IBM Plex Mono Bold', monospace; ``` ## How to Override Fonts ### 1. Connecting Your Own Fonts #### Using Google Fonts If you want to use fonts from Google Fonts, add the following line in your HTML file inside the `` tag: ```html ``` #### Using Local Fonts If you prefer to use local fonts, add them using the @font-face rule in your CSS file: ```css @font-face { font-family: 'MyLocalFont'; src: url('path/to/font.woff2') format('woff2'), url('path/to/font.woff') format('woff'); font-weight: normal; font-style: normal; } ``` ### 2. Overriding CSS Variables After connecting the fonts, override the default variables with your custom font values in your CSS file: ```css :root { --font-main: 'YourFont', sans-serif; --font-main-semibold: 'YourFont SemiBold', sans-serif; --font-main-bold: 'YourFont Bold', sans-serif; --font-editor: 'YourEditorFont', monospace; --font-editor-semibold: 'YourEditorFont SemiBold', monospace; --font-editor-bold: 'YourEditorFont Bold', monospace; } ``` ## Conclusion Customizing the fonts in our application is simple and convenient. By following the instructions above, you can configure the appearance of your application to your preferences. If you want to configure fonts on canvas, you can read how to do it [here](dxcharts-lite/Fonts) --- ## dxcharts-react/keyboard-shortcuts.mdx import { KeyboardShortcuts } from './code/KeyboardShortcuts'; # Keyboard Shortcuts DXChart has keyboard shortcuts to the most of its functionality and knowing these hotkeys can help you while using chart. > Note: There is a small possibility some of the hotkey shortcuts won't work or work incorrectly if the keyboard layout is not English. ## Table of keyboard shortcuts Here is the full list of DXChart keyboard shortcuts: --- ## dxcharts-react/Loading state.mdx import { ChartReactAppWithLoadingState } from './code/loading-state'; import code from '!!raw-loader!./code/loading-state'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import InitialLoading from '!!raw-loader!../../../../chart-react/src/chart/view-models/loading/initial-loader.vm'; # Initial Loading State Loading state is a way `dxcharts` splits the initial chart data loading into chunks and fills the initial loader progress bar. It consists of `InitialLoadingItem` array, some of them are necessary, but there is also a possibility to add custom ones. ## Additional loading states Let's add additional loading state on [[API Reference - Providers|chartDataProvider.requestHistoryData]] candles data load ## Further reading - [[API Reference - Providers|API Reference - Providers]] --- ## dxcharts-react/Localization.mdx --- tags: localization --- import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; import Localization from '!!raw-loader!./code/localization.tsx'; # Localization Our `localization` dependency is a main entry point to provide human readable strings to our `dxchart5-react` app. ## Custom localization > We highly recommend using `TypeScript`, because IDE will help with suggestions and it will be a lot easier to escape missing locale strings. To provide your own `localization` object you should do the following: {Localization} --- ## dxcharts-react/Multichart.mdx --- tags: multichart --- # Multichart DXCharts has a multichart functionality, i.e. you can add up to 8 charts on the screen Each of the charts would be a _separate instance_ of [[Quick Start - React|Chart React]] ## Multichart layouts There are currently 7 layouts available: - 1x1 - 2x2 - 2x1 - 1x2 - 3x1 - 1x3 - 2x4 ## Synchronization options Though separate charts its useful to synchronize some of their data There are several options: - Instrument - Chart type - Aggregation period - Appearance - Studies When turning sync ON all charts will share the selected option > When syncing period, charts will also move synchronously by X axis, having the same timeframe viewport ## Styling Dropdown component for switching multichart UI is `MultichartSettingsDropdown` The general idea of styling customization is the same - cascading styles using `styled-components` package For example, we need to style `MultichartSettingsDropdown` content. Because `MultichartSettingsDropdown` is wrapped with the `context` we can't just use `styled(MultichartSettingsDropdown)`. Moreover, we have a problem, because `Dropdowns` by default are mounted to the `HTMLElement` with the `id="root"`, so, to make cascade styling of `dropdown` with `styled-components` works correctly, we have two ways: 1. (Preffered) To Style `dropdown`, use the highest component, where the `id=root`. Then everything will work fine. 2. (for custom cases) This one only works when you use `Dropdown` component directly in a render function. Every dropdown accepts `container?: Element` as a property, so if you need to style `dropdown` from here, you can pass your own `container` for `dropdown`, and style it from here. But you can always return to the 1. ## Further reading [[Styling]] --- ## dxcharts-react/Multiple Scales/Overview.mdx # Overview > It is recommended to read [multiple scales in dxcharts-lite](dxcharts-lite/Multiple-Scales/Overview) article first to better understand it's core functionality. > Moving data series in dxcharts-react is designed only for studies data series and with specific behavior, if you want to move some other data series without this behavior you should use dxcharts-lite [pane manager](dxcharts-lite/API-Reference/PaneManager) method moveDataSeriesToPane `dxcharts-react` [API](dxcharts-react/Quick-Start/Quick-Start---API) allows to move study data series inbetween [panes](dxcharts-lite/Panes) and also move to any scale: new or already existing within a single pane with these methods: - `moveStudyToPane` - move study data series to the pane above or below, there are two non-ordinary scenarios if using this method: 1. Pane is not created yet, then it creates a new one 2. Target pane is the main "chart" pane with candles series, if so, the new scale for the study will be created if this study has `overlaying` property as `false` by default, otherwise, it will be moved to the same as the main candle series scale - `moveStudyToNewScaleWithinChartPane` - move study data series to the new scale to the left or right of a chart - `moveStudyToExistingScaleWithinChartPane` - move study data series to the existing y axis scale and could be found on `dxcharts-react` [API page](dxcharts-react/API-Reference---API) ## Further reading - [Multiple scales in dxcharts-lite](dxcharts-lite/Multiple-Scales/Overview) - [Pane manager API in dxcharts-lite](dxcharts-lite/API-Reference/PaneManager) - [Panes API in dxcharts-lite](dxcharts-lite/API-Reference/PaneComponent) - [dxcharts-react API](dxcharts-react/API-Reference---API) --- ## dxcharts-react/NavigationMap.mdx --- tags: navigation-map --- import { NavigationMapExample } from './code/navigationMap.tsx'; import code from '!!raw-loader!./code/navigationMap.tsx'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; # Navigation Map Navigation map is a component which allows to see the entire chart data series and navigate through it. It consists of a **slider**, which shows a certain amount of data, **knots**, which are used to change slider's length and **arrow buttons** for precise navigation. ## Configuration Visibility of the navigation map could be configured via initial dependencies config: ```ts initialChartConfig: { components: { navigationMap: { visible: true, }, }, }, ``` ## Example ## Further Reading - [Navigation map in dxcharts-lite](dxcharts-lite/NavigationMap) - [Navigation map component API](dxcharts-lite/API-Reference/NavigationMapComponent) --- ## dxcharts-react/Palette.mdx # Customizing Chart Using a Color Palette ## Overview This document describes the mechanism for customizing the application using color palettes. The application allows setting your own color palette through the `palette` dependency. This enables you to adjust the appearance of the component for different color schemes. ## Introduction The `ChartReactApp` application supports customization of the color palette through a specialized property called `palette`, which should be passed in the dependencies. This property allows users to override the default colors. Custom color palettes for dark and light themes are set through the `palette` variable, which contains two nested structures: dark and light. Each represents an object with specific color properties. ## Palette Definition Example > The palette objects support all common color models: hex, rgb/rgba, hsl/hsla, default strings ('green', 'yellow', etc...), but the most native for chart is rgba, so it's recommended to use it if possible ```typescript export interface ChartAppPalette { light: { object: ChartPaletteLight; }; dark: { object: ChartPaletteDark; }; } export type ChartPaletteLight = typeof DEFAULT_CHART_PALETTE_LIGHT; export const DEFAULT_CHART_PALETTE_LIGHT = { 'main_chart-bg': 'rgba(255,255,255,1)', 'button-border-bg': 'rgba(229,229,229,1)', 'button-buy-default-bg': 'rgba(210,231,212,1)', 'button-buy-default-color': 'rgba(107,175,113,1)', 'button-buy-hovered-bg': 'rgba(232,241,233,1)', 'button-focus-border': 'rgba(255,91,36,1)', // other properties... }; export type ChartPaletteDark = typeof DEFAULT_CHART_PALETTE_DARK; export const DEFAULT_CHART_PALETTE_DARK = { 'main_chart-bg': 'rgba(0,0,0,1)', 'button-border-bg': 'rgba(40,40,40,1)', 'button-buy-default-bg': 'rgba(31,47,33,1)', 'button-buy-default-color': 'rgba(77,153,83,1)', 'button-buy-hovered-bg': 'rgba(43,73,45,1)', 'button-focus-border': 'rgba(255,170,0,1)', // other properties... }; ``` ## Selective Color Overriding You can also override individual colors rather than replacing the entire palette. This provides more flexibility, allowing you to customize specific parts of the application without having to define all color properties. #### Code Example: Overriding Individual Colors ```typescript import React, { memo } from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const Container = memo(props => { const deps = { ...CREATE_MOCK_PROVIDERS(), palette: { dark: { object: { 'main_chart-bg': 'blue', 'button-border-bg': 'green' }, }, light: { object: { 'main_chart-bg': 'red', 'button-border-bg': 'yellow' }, }, }, }; return ; }); ``` ## Conclusion By using the `palette` dependency, you can flexibly change the interface colors for both dark and light themes, allowing you to adapt the appearance of the application to specific needs and user preferences. Additionally, you can override individual colors instead of the entire palette, giving you finer control over the appearance of your components. > To see the full possibilities of coloring, please check out the [[Theme Builder|Theme Builder]]. --- ## dxcharts-react/Performance/DXChart optimizations.mdx --- tags: performance --- import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import example from '!!raw-loader!./extract-data-requests.example.ts'; # DXChart Optimizations ## Prefetching chart data optimization You can use `extractDataRequests` function for prefetch chart data optimization on initial loading. The request to data provider is made only after chart initialization (1-2 seconds), so in this case you might want to request data from server in advance. However, your data provider must implement caching mechanism, because when the chart will be initialized it will call the data provider for the data which should be already cached. Here is an example of how you can use `extractDataRequests` function: {example} --- ## dxcharts-react/Performance/extract-data-requests.example.ts ```ts /* eslint-disable no-restricted-syntax */ import { ChartDataProvider } from '@dx-private/dxchart5-react/dist/providers/chart-data-provider'; import { ChartLayoutData } from '@dx-private/dxchart5-react/dist/providers/layout-provider'; import { extractDataRequests } from '@dx-private/dxchart5-react/dist/utils/extract-data-requests.util'; // these are just placeholders, you should replace them with your actual data provider and layout data const chartDataProvider = {} as ChartDataProvider; const layoutData = {} as ChartLayoutData; const requests = extractDataRequests(layoutData); requests.forEach(r => chartDataProvider.requestHistoryData(r.symbol, r.aggregation, r.options)); ``` --- ## dxcharts-react/Playgrounds/Dependecies graph.mdx import { VisNetworkGraph } from '../../../components/vis-network/vis-network'; --- ## dxcharts-react/Playgrounds/Theme Builder.mdx import { ChartThemeBuilder } from '../../../components/chart-theme-builder/chart-theme-builder'; # dxchart5-react Theme Builder Playground > Some variables might not apply any visual changes to the chart app, because they are currently not used for various reasons, but they still are a part of the palette and could be used if needed The playground allows to try different custom coloring for the dxchart5-react application. ## Further reading - [[Theme Builder - Lite|Chart Lite Theme Builder]] --- ## dxcharts-react/Price Precision.mdx # Price Precision Price precision in other words means how many decimal numbers you will see for a particular instrument price on Y Axis. In `dxchart5-react` we have a special concept for this called `priceIncrements`. `priceIncrements` is an array of numbers that represent the **price steps** for an instrument on Y-Axis in the next format: ``` [(priceIncrement, price)*, incrementForOtherPrices] ``` ### Example Let's take a look on the example for better understanding: ```ts const priceIncrements = [0.01, 1, 0.1, 10, 1]; ``` Here we can see three increments: - (for the **price less than 1**, the price step is **0.01**) - (for the **price less than 10**, the price step is **0.1**)` - and (for **any other price**, the price step is **1**). This means that for the **price less than 1**, the price will be shown with **2 decimal numbers**, for the **price less than 10**, the price will be shown with **1 decimal number**, and for **any other price**, the price will be shown **without decimal numbers**. **NOTE**: there are some requirements for `priceIncrements` array: - minimum length is **3**. - number of elements in the array should be **odd**. ### How to control `priceIncrements` in `dxchart5-react` Each instrument should have its own `priceIncrements` to display the price on Y-Axis correctly, if no `priceIncrements` was provided to `Instrument` it defaults to `0.01` for all price ranges. In `dxchart5-react` the list of available `Instrument`s is passed via `SymbolSuggestProvider` ([reference](dxcharts-react/Providers/symbol-suggest-provider)). Each `Instrument` has a `priceIncrements` property that you can use to control the price precision. ```ts const instruments = [ { description: '"Advanced Micro Devices, Inc. - Common Stock"', priceIncrements: [0.0001, 1, 0.01], symbol: 'AMD', type: 'STOCK', }, ]; ``` --- ## dxcharts-react/Providers/Chart Data Provider.mdx import MockChartDataProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-chart-data-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # Chart Data Provider `ChartDataProvider` - is the main data provider in `dxchart5-react`. It's responsible for historical candles, last candle updates, quotes, etc. > NOTE: it is the only entry point for your candles data. ## Example {MockChartDataProviderCode} ## More details regarding `requestHistoryData` > The information below might be useful to help understanding the data fetch process inside candles provider `dxchart5-react` is a data-source agnostic library, any arguments `dxchart5-react` provides to data provider methods are not query parameters to the data source in most cases. `fromTime` and `toTime` arguments simply reflect the current state of the chart, not the chart request to load additional data. Therefore, you can either use the current state of the chart to calculate the query parameters you need for your data source, or completely ignore them. > Both options: `fromTime` and `toTime` are optional and could be `undefined`. #### `fromTime` As was said, `fromTime` is an optional argument and not passed to `options` argument in most of the cases at all. Also, there is NO difference between the `undefined` and `0` value. When `fromTime` is `undefined || 0` (in most of the cases) it shouldn't be interpreted by the provider code as _that data should be fetched since the beginning of that instrument_. On the very first load, when user haven't performed any scrolling actions and there's no any user-related/layout-related data from previous chart usage, you can just load latest dataset. In this case, the rightmost candle will be the current moment - `Date.now()` and the leftmost candle will be **any date** in the past you want to be loaded. On the second load, when chart was already loaded with some data (after very first load), you'll receive `fromTime` argument which is the leftmost candle timestamp of previously loaded data. In addition, `fromTime` is usually used by timeframe presets functionality, so you will see `fromTime` parameter provided when user selects some timeframe preset. #### `toTime` For **lazy loading** only `toTime` is important because it identifies the oldest candle already loaded and passed to `dxchart5-react` to determine till which timestamp more candles need to be loaded. Number of lazy loaded candles can be defined by the provider itself. If user will scroll the chart to the left, deep in the history data, your provider will receive `toTime` argument which will be a timestamp of leftmost candle in viewport. So, to load additional set of candles you should use `toTime` as a rightmost cut-off of data, and leftmost cut-off you can calculate based on the desired amount of data. #### `updateCallback` `updateCallback` allows to completely **override** initial history data if you need to update it after some period of time. > Since `updateCallback` function **overrides** all history data, make sure you provided **all history data with updates**, **NOT** just updates. When chart asks you a data (for example on load or when you change aggregation/instrument) via `requestHistoryData` you should provide history data via return value, pretty simple. BUT when you want to change data after some time after initial `requestHistoryData` was called, then you should use `updateCallback`. ```typescript // ...your `ChartDataProvider` code const initialHistoryDataForSelectedInstrument = [...] // store `updateCallback` somewhere to access it out of requestHistoryData scope if you need let updateHistoryCallback = undefined; // imagine that after some period of time you need to update some part of history data for selected instrument setTimeout(() => { const historyDataUpdates: ChartCandleData[] = [ { time: 1719218004, open: 10, high: 20, low: 10, close: 15, volume: 30000 }, ]; // implement your function that will merge desired history data updates with initial history data const mergedHistoryData = yourMergeFunction(initialHistoryDataForSelectedInstrument, historyDataUpdates) // then pass merged data to `updateCallback` updateHistoryCallback(mergedHistoryData) }, 10000); const requestHistoryData = ( symbol: string, aggregation: AggregationPeriod, options: RequestMoreDataOptions & ChartDataOptions, updateCallback: (data: ChartCandleData[]) => void, chartId: string, ) => { updateHistoryCallback = updateCallback; // average provider code, // same as in the example above let toTime = options?.toTime; let fromTime = options?.fromTime; if (!toTime) { toTime = Date.now(); } if (!fromTime) { fromTime = toTime - periodToMinutes(aggregation) * 60 * 1000 * NUMBER_OF_CANDLES; } const data = initialHistoryDataForSelectedInstrument; return new Promise(resolve => { setTimeout(() => resolve(data), 3000); }); }; ``` `requestHistoryData` provides `ChartCandleData[]`, that have some options for indicators, for example `typicalPrice` field, that provides initial calculated data for `Volume Weighted Average Price` indicator. > Please note that the VWAP indicator requires trading sessions data for its calculations, regardless of whether you are providing the data for typicalPrice in candle or not. Therefore, you need to create a `TradingSessionsProvider` using data generated from `generateSessions`. ```typescript const historyDataMap = (candle: Candle) => ({ time: candle.timestamp, open: candle.open, high: candle.hi, low: candle.lo, close: candle.close, volume: candle.volume, impVolatility: 0, vwap: 0 // deprecated, use typicalPrice field to calculate VWAP study typicalPrice: 0 // here you can provide your own typical price calculation or provide from other source, otherwise you can left this field undefined, usually typical price equals (high + low + close) / 3 }); ``` ## API Reference ## Further reading - [Symbol Suggest Provider](dxcharts-react/Providers/symbol-suggest-provider) --- ## dxcharts-react/Providers/Chart Sharing Provider.mdx import MockChartSharingProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-chart-sharing-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # Chart Sharing Provider `ChartSharingProvider` is a way to share user's chart screenshots with other people across the Internet. `ChartSharingProvider` is only provides you a `Blob` with the screenshot of the chart. You can then operate with this `Blob` as you want. Basically for an integration you need to use/implement some image hosting service, that will store given `Blob` and give you an `URL` for that `Blob` in turn, which then can be used by users to access the screenshot. ## Example {MockChartSharingProviderCode} ## API Reference --- ## dxcharts-react/Providers/Events Data Provider.mdx import MockEventsDataProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-events-data-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import EventsUI from './img/eventsUI.png'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # Events Data Provider `EventsDataProvider` connects your economic events (such as earnings, dividends, splits, etc.) provider for selected instrument with `dxchart5-react` application. Here's how they look like on chart: ## API Reference ## Example {MockEventsDataProviderCode} --- ## dxcharts-react/Providers/Indicator Templates Provider.mdx import LocalStorageIndicatorTemplateProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/localstorage/localstorage-indicator-template-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; import IndicatorTemplatesUI from './img/indicatorTemplatesUI.png'; # Indicator Templates Provider `IndicatorTemplatesProvider` is responsible for saving and retrieving the _Indicator Templates_. _Indicators Template_ is a set of indicators combined together, which can be easily applied to the chart in one click. This feature significantly increases the user experience, as user can create multiple templates for different purposes and switch between them quickly. This is how it looks on chart: And like any other _provider_, `IndicatorTemplatesProvider` is a way to connect your storage of user's `Indicator Templates` with `dxchart5-react` application. ## API Reference ## Example Here's an implementation of `IndicatorTemplatesProvider` that stores users templates in the `localStorage`. {LocalStorageIndicatorTemplateProviderCode} --- ## dxcharts-react/Providers/Layout Provider.mdx import LocalStorageLayoutProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/localstorage/localstorage-layout-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # Layout Provider `LayoutProvider` is responsible for saving and retrieving the _Layout_ of the chart. What is a _layout_ in `dxchart5-react`? _Layout_ is a kind of a snapshot of current user layout and application state. User can create multiple layouts to switch between them quickly, which is nice for user experience. User can update already created layouts if he changes his mind for some reason. And all of these actions should be reflected in some storage. `LayoutProvider` is a way to make a channel between your storage and `dxhcart5-react` application. ## Example To better illustrate this concept here's an example of the `LayoutProvider` that stores `LayoutData` in the `localStorage`. {LocalStorageLayoutProviderCode} ## API Reference ## Further reading - [User Data Provider](dxcharts-react/Providers/user-data-provider) --- ## dxcharts-react/Providers/News Data Provider.mdx import MockNewsDataProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-news-data-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import NewsUI from './img/newsUI.png'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # News Data Provider `NewsDataProvider` connects your news regarding selected instrument with `dxchart5-react` application. Here's how they look like on chart: ## Example {MockNewsDataProviderCode} ## API Reference --- ## dxcharts-react/Providers/Symbol Suggest Provider.mdx import MockSymbolSuggestProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-symbol-suggest-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # Symbol Suggest Provider `SymbolSuggestProvider` is responsible for retrieving instruments data for `dxchart5-react`. There's multiple cases, when `SymbolSuggestProvider` is used: - on initial chart load to retrieve info about `initialInstrument` provided via `initialChartConfig` or from `Layout` if exists - when user types in the search input (main instrument and compare instruments) to get suggestions for instruments Apart from instrument's `symbol`, `description` and `type`, `SymbolSuggestProvider` also responsible for providing some more useful info about instrument: #### `tradingHours` `tradingHours` is, obviously, trading hours for the instrument. You should provide a `string` in a special, well known, format, for example: ```typescript const symbol = 'DANOY'; const tradingHours = 'OOTC(name=OTC Other;tz=America/New_York;hd=US;sd=U…5;de=+0000;rt=0300;0=p08000930r09301600a16002005)'; ``` #### `priceIncrements` `priceIncrements` is an array of numbers that represent the **price steps** for an instrument on Y-Axis in the next format: > Read more about `priceIncrements` in [Price Precision](dxcharts-react/price-precision) ## Example {MockSymbolSuggestProviderCode} ## API Reference ## Further reading - [Layout Provider](dxcharts-react/Providers/layout-provider) --- ## dxcharts-react/Providers/Trading Sessions Provider.mdx import MockTradingSessionsProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-trading-sessions-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # Trading Sessions Provider `TradingSessionsProvider` is responsible for getting selected instrument's trading schedule. > `dxchart5-react` requests sessions up to +14 days from `Date.now()` for a correct work, so make sure you've provided enough. There's only 4 types of trading sessions supported: ```typescript type SessionType = 'NO_TRADING' | 'PRE_MARKET' | 'REGULAR' | 'AFTER_MARKET'; ``` `TradingSessions` incorporates `from` and `to` timestamps, which is a range of time when the session is active, and `type` of the session. ```typescript const tradingSessions: TradingSession[] = [ { // as you can see here, NO_TRADING session is defined as from 12:00 AM to 8:00 AM on 10 Jul from: 1720569600000, // GMT: Wednesday, July 10, 2024 12:00:00 AM to: 1720598400000, // GMT: Wednesday, July 10, 2024 8:00:00 AM type: 'NO_TRADING', }, ]; ``` What you should do is just to define instrument's trading sessions one by one, one after another, like: ```typescript const tradingSessions: TradingSession[] = [ { // as you can see here, you define NO_TRADING session from 12:00 AM to 8:00 AM on 10 Jul from: 1720569600000, // GMT: Wednesday, July 10, 2024 12:00:00 AM to: 1720598400000, // GMT: Wednesday, July 10, 2024 8:00:00 AM type: 'NO_TRADING', }, { // after that obviously PRE_MARKET sessions starts from 8:00 AM to 12:00 PM from: 1720598400000, // GMT: Wednesday, July 10, 2024 8:00:00 AM to: 1720612800000, // GMT: Wednesday, July 10, 2024 12:00:00 PM type: 'PRE_MARKET', }, { // then REGULAR session starts from 12:00 PM to 8:00 PM from: 1720612800000, // GMT: Wednesday, July 10, 2024 12:00:00 PM to: 1720641600000, // GMT: Wednesday, July 10, 2024 8:00:00 PM type: 'REGULAR', }, { // and AFTER_MARKET then starts from 8:00 PM Jul 10 to 12:00 AM Jul 11 from: 1720641600000, // GMT: Wednesday, July 10, 2024 8:00:00 PM to: 1720656000000, // GMT: Thursday, July 11, 2024 12:00:00 AM type: 'AFTER_MARKET', }, { // and so on... from: 1720656000000, to: 1720684800000, type: 'NO_TRADING', }, ]; ``` > With tradable _weekends_ and _holidays_ the situation is the same - just define `TradingSession` in the same format for them. ## Example {MockTradingSessionsProviderCode} ## API Reference --- ## dxcharts-react/Providers/User Data Provider.mdx import LocalStorageUserDataProviderCode from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/localstorage/localstorage-user-data-provider.js'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { DedokMethods } from '../../../components/dedok/DedokMethods/DedokMethods'; # User Data Provider `UserDataProvider` is kinda the same as [LayoutProvider](dxcharts-react/Providers/layout-provider) but for user defined data - `UserData`. As a `LayoutProvider` `UserDataProvider` is a way to connect your storage with `dxchart5-react` application. What data user can define in `dxchart5-react`? - custom aggregations - custom colors from color picker - custom timeframe presets - favorite drawings - recent drawings - positions of elements such as `drawings toolbar`, `chart layers popover element` - drawings sidebar state ## Example Here's an implementation of `UserDataProvider` that stores `UserData` in the `localStorage`. {LocalStorageUserDataProviderCode} ## API Reference --- ## dxcharts-react/Quick Start/Quick Start - API.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import QuickStartAPIReact from '!!raw-loader!./code/quick-start-api-react.tsx'; import QuickStartAPiCreateChart from '!!raw-loader!./code/quick-start-api-createChart.ts'; # `dxchart5-react` API `dxchart5-react` provides a powerful API to do things with the application in runtime. > Full list of supported API can be found [[API Reference - API|here]]. ## How to use `dxchart5-react` API Our API object is created only after the `dxchart5-react` application is created, this way we garantee that everything will work as expected. Due to that fact our API object is provided as an argument to a callback function `onApiCreated`, so you could store reference to the API somewhere in your application. To show you how it works let's create an example where we set the instrument to `IBM`, change the aggregation to `1h` and set chart app theme to `light`. ### using `` react component To store API object reference in `react` based app you can use `useRef` concept or `useState` - it depends on your needs. {QuickStartAPIReact} ### using `createChart` function Same thing with `createChart` function, but to store API object reference we will use plain variable. {QuickStartAPiCreateChart} ### Internal API In that article we described `supported` API which we "support" and guarantee that it works. If you haven't found API method you need you can try [Internal API](dxcharts-react/Advanced/Internal-API) - it gives you access to all `dxchart5-react` internal codebase. ## Further reading - [[Quick Start - Configuration|Configuration]] - [[Quick Start - Providers|Providers]] --- ## dxcharts-react/Quick Start/Quick Start - Angular.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp'; # How to use `dxchart5-react` with Angular based apps To use a React library inside an Angular component, we'll need to embed React components within Angular. Here’s a step-by-step guide to get you going: ### Install all necessary libraries in your Angular project ```bash npm install react npm install react-dom npm install @devexperts/dxcharts-lite npm install @dx-private/dxchart5-modules npm install @dx-private/dxstudies npm install @dx-private/dxchart5-react npm install @dx-private/dxchart5-react-mock-providers (Optional) ``` ### We need to create 3 files - MyReactWrapper.tsx ```js import React from "react"; import { ChartReactApp } from "@dx-private/dxchart5-react/dist/chart/chart-react-app"; import { CREATE_MOCK_PROVIDERS } from "@dx-private/dxchart5-react-mock-providers/dist"; interface Props { someProp: string; } const MyReactWrapper: React.FC = () => { return (
); }; export default MyReactWrapper; ``` - renderReactComponent.tsx ```js import React from 'react'; import ReactDOM from 'react-dom/client'; import MyReactWrapper from './MyReactWrapper'; export function renderReactComponent(element: HTMLElement, props: { someProp: string }) { const root = ReactDOM.createRoot(element); root.render(); return () => root.unmount(); // cleanup function } ``` - react-wrapper.component.ts (Angular file) ```js import { Component, ElementRef, AfterViewInit, OnDestroy, ViewChild, } from '@angular/core'; import { renderReactComponent } from './renderReactComponent'; @Component({ selector: 'app-react-wrapper', template: `
`, }) export class ReactWrapperComponent implements AfterViewInit, OnDestroy { @ViewChild('reactContainer', { static: true }) containerRef!: ElementRef; private cleanup: (() => void) | undefined; ngAfterViewInit() { this.cleanup = renderReactComponent(this.containerRef.nativeElement, { someProp: 'Hello from Angular!', }); } ngOnDestroy() { if (this.cleanup) { this.cleanup(); } } } ``` After that just include `` in your index.html Your package.json file should look something like this ```json { "dependencies": { "@angular/common": "^19.2.0", "@angular/compiler": "^19.2.0", "@angular/core": "^19.2.0", "@angular/forms": "^19.2.0", "@angular/platform-browser": "^19.2.0", "@angular/platform-browser-dynamic": "^19.2.0", "@angular/router": "^19.2.0", "@devexperts/dxcharts-lite": "^2.7.6", "@dx-private/dxchart5-modules": "^5.14.7", "@dx-private/dxchart5-react": "^5.14.7", "@dx-private/dxstudies": "^59.0.1747920660503", "@dx-private/dxchart5-react-mock-providers": "1.0.1", "react": "18.3.0", "react-dom": "18.3.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, "devDependencies": { "@angular-devkit/build-angular": "^19.2.13", "@angular/cli": "^19.2.13", "@angular/compiler-cli": "^19.2.0", "@types/jasmine": "~5.1.0", "@types/react": "^19.1.6", "@types/react-dom": "^19.1.5", "jasmine-core": "~5.6.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", "typescript": "~5.7.2" } } ``` Then you should see the following. That's the bare minimum usage with mock data. ### Issues that you might run into - Module './renderReactComponent' was resolved to 'angular-app/src/app/renderReactComponent.tsx', but '--jsx' is not set.ts(6142) This error happens because TypeScript doesn’t know how to handle .tsx files by default in an Angular project, since Angular projects typically use .ts files only and don’t enable JSX support. In order to fix this navigate to tsconfig.json and add ```js "compilerOptions": { "jsx": "react-jsx", ``` - Inside of renderReactComponent.tsx I get this error Internal server error: Failed to resolve import "react-dom/client" from ".angular/vite-root/angular-app/main.js". Does the file exist? Angular app is running with Vite, and Vite doesn't recognize react-dom/client (or other React modules) unless they are properly installed and Vite is configured to handle React correctly. The react-dom/client import is only available in React 18+ In this case just update to React 18+, if are on an older version you can use a "react-compatible" vite plugin such as "@vitejs/plugin-react" ## How to configure `dxchart5-react` library `dxchart5-react` has a very strong configuration abilities, most of the time you would need to change it for your needs. > All the configurable stuff could be found on [configuration reference page](dxcharts-react/Quick-Start/Quick-Start---Configuration). ## Further reading - [[Quick Start - Configuration|Configuration]] - [[Quick Start - API|API]] - [[Quick Start - Providers|Providers]] - [[Quick Start - Appearance|Appearance]] --- ## dxcharts-react/Quick Start/Quick Start - Appearance.mdx # How to change appearance of `dxchart5-react` ### Fully custom CSS `dxchart5-react` uses `styled-components` (SC) under the hood, so you can use `SC` style overrides and change appearance of your app. [[Styling|Read more]] > Styling with `styled-components` is only available for `react` components. ### CSS variables For colors we use `CSS` variables - so just replace them to change the look and feel of your app. ### Styling the `dxcharts-lite` canvas library In `dxcharts-lite` we have [config](dxcharts-lite/Chart-Config) which is used to change colors and a lot of different stuff. You can access it via `dxchart5-react` dependencies. [Read More](dxcharts-react/Quick-Start/Quick-Start---Configuration) --- ## dxcharts-react/Quick Start/Quick Start - Configuration.mdx import { RenderDedokTable } from '../../../components/dedok/DedokTable/DedokTable'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import QuickStartConfigReact from '!!raw-loader!./code/quick-start-configuration-react.tsx'; import QuickStartConfigCreateChart from '!!raw-loader!./code/quick-start-configuration-createChart.ts'; import { ChartConfigurationDemo } from './code/quick-start-configuration-demo.tsx'; # `dxchart5-react` Configuration `dxchart5-react` has a very strong configuration abilities, most of the time you would need to change it for your needs. Configuration for `dxchart5-react` should be provided via `dependencies`. These dependencies consists of some meaningful parts: - initials - configs - providers ### Initials This part of `dependencies` is responsible for configuration of the `initial` chart appearance and functionality. It means that every new user will see the chart with exactly this configuration. Important note: user **CAN** change this stuff interacting with the app. Here's the list of available options: ### Configs With these options you can change the UI, localization, palette and other parts of the app. Important note: user can**NOT** change this stuff interacting with the app. Here's the list: ### Providers Providers are aimed to send data to `dxchart5-react` and vice-versa such as candles, symbols, user data and so on. You can learn more about the `providers` concept [[Quick Start - Providers|here]]. ## Example To give you a brief introduction how you can configure the `dxchart5-react` let's hide drawings sidebar and enable tooltips for the buttons in the toolbar. ### using `` react component For `react` based application `dependencies` should be passed via react component props: {QuickStartConfigReact} ### using `createChart` function If you use `createChart` function to render `dxchart5-react` app, it's easy to pass the `dependencies` too: {QuickStartConfigCreateChart} With this example you should see the following: ## Further reading - [[Quick Start - API|API]] - [[Quick Start - Providers|Providers]] --- ## dxcharts-react/Quick Start/Quick Start - Providers.mdx --- tags: provider, custom provider --- import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import ChartReactAppWithMockProvidersDemo from '!!raw-loader!./code/quick-start-providers-react.tsx'; import { createMockChartDataProvider } from '@dx-private/dxchart5-react-mock-providers/dist'; import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp.tsx'; # `dxchart5-react` Providers With providers you can send data to `dxchart5-react` and vice-versa such as candles, symbols, user data and so on. Any time you have HTTP call or any other external data fetching, or you have some external storage that should be wired up to `dxchart5-react` - it's a provider job. **All providers are required** to make chart work and you need to implement them on your own. > For demo purposes you can use our mock implementations of the providers from `@dx-private/dxchart5-react-mock-providers` package. `dxchart5-react` has a strict interfaces for a providers, and **your implementation of the provider should satisfy that interface** to make it work. ## Mock providers from `@dx-private/dxchart5-react-mock-providers` To help you start with providers we've created a set of **mock** providers that you can use in your application. This will help you to incrementally implement your own set of providers and still have a working chart. {ChartReactAppWithMockProvidersDemo} That's pretty much it! After this you will see working chart with mock data: ## Further reading To help you implement your own providers there's a guides for each of them with code examples. You can start your journey from [Chart Data Provider](dxcharts-react/Providers/chart-data-provider) as the main purpose of chart is to show data. Then move to [Symbol Suggest Provider](dxcharts-react/Providers/symbol-suggest-provider) and so on. > For a quick `API` reference you can check out the [[API Reference - Providers|providers API reference page]] --- ## dxcharts-react/Quick Start/Quick Start - React.mdx import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import QuickStart from '!!raw-loader!./code/quick-start-react.tsx'; import ChartDataProvider from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-chart-data-provider'; import SymbolSuggestProvider from '!!raw-loader!@dx-private/dxchart5-react-mock-providers/dist/mock/mock-symbol-suggest-provider'; # How to use `dxchart5-react` with React based apps For `react` based apps we have a `react` component `ChartReactApp` and to start you simply need to render it like any other `react` component. ### Attach `ChartReactApp` component to your application {QuickStart} Container element of the `ChartReactApp` component should have the `height` and `width` in absolute units such as `px`, or `vh`, because that's how `CSS` works. So to make it display right, just give it a height `576px` and width `800px` for example. Then you should see the following. This is the minimum version of the chart with mock data. ## Further reading - [[Quick Start - Configuration|Configuration]] - [[Quick Start - API|API]] - [[Quick Start - Providers|Providers]] - [[Quick Start - Appearance| Appearance]] - [Component overrides](dxcharts-react/Component-Overriding/Overview) --- ## dxcharts-react/Quick Start/Quick Start - Vanilla.mdx import { ChartReactApp } from '../../../components/utils/ChartReactApp'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { ChartWidget } from '../../dxcharts-react-widget/script/chart-widget.tsx'; import chartReactApp from '!!raw-loader!../../../../../chart-react/src/chart/chart-react-app'; import chartReactWidgetConfig from '!!raw-loader!../../../../../chart-react-widget/src/widget.config.ts'; import { ChartConfigurationDemo } from './code/quick-start-configuration-demo.tsx'; import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import QuickStartConfigCreateChart from '!!raw-loader!./code/quick-start-configuration-createChart.ts'; # How to use `dxchart5-react` with vanilla apps You can use `dxchart5-react` as script, by importing it to your html file, or via npm. ## Adding `dxchart5-react` as script For simple applications, that don't use any package manager such as `npm` and wire up third party packages via ` ``` Here's a very basic example: ```html DXCharts Vanilla JS
``` After this, you should see the following: ### How to configure `dxchart5-react-widget`. Full configuration you can find in `./dxchart.widget/dist/widget.config.d.ts` It contains all options for initial chart configuration. For example, we configured `width` and `height`, `symbol`, and `chartType` for chart: ```js ``` ### How to use API If you want to change something on chart from your host app, you can use `onAPICreated` dependency, and pass it to chart. Full list of available supported API is [here](dxcharts-react/API-Reference---API) Take a look at example: ```js ``` ## Adding `dxchart5-react` via npm First of all you need to install library via npm. Please take a loook how to do it [here](dxcharts-react/Quick-Start/Quick-Start) After you should import a special function `createChart`, which attaches the `ChartReactApp` component to a given HTML element. ```js import { createChart } from '@dx-private/dxchart5-react/dist/index'; // append container somewhere in your app const container = document.createElement('dxcharts-container'); createChart(container); ``` ### Configuring Second argument of function `createChart` passes configuration object. Take a look at example of using: {QuickStartConfigCreateChart} With this example you should see the following: ### How to use API There is a simple way to use APi, similar with chart script implementation: ```ts import { createChart } from '@dx-private/dxchart5-react/dist/index'; // append container somewhere in your app const container = document.createElement('dxcharts-container'); const onAPICreated = api => { // Log a message to the console when the chart is created api.onChartCreated((chartId, chart) => { console.log(`Chart with id ${chartId} created and ready to use api!`); }); // Change the theme of the chart to 'light' api.supported.changeTheme('light'); // Send a notification message to the top of the chart api.supported.sendNotification('Your notification message at the top of the chart!'); // ...other code, depends on your purposes }; // take a look: function `createChart` returns an object with method `destroy`, // that allows you to unmount chart at any time const destroyChart = createChart(container, { onApiCreated, dependencies: { // ...your configuration here }, }); // ...another place in your code, you decided to unmount chart after some action destroyChart.destroy(); ``` ## Further reading - [[Quick Start - Configuration|Configuration]] - [[Quick Start - Providers|Providers]] - [[Quick Start - Appearance|Appearance]] - [[Quick Start - API|API]] --- ## dxcharts-react/Quick Start/Quick Start - Vue.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp'; # How to use `dxchart5-react` with Vue based apps For applications that doesn't use `react` as a UI framework `dxchart5-react` package has a special function `createChart`, which attaches the `ChartReactApp` component to a given HTML element. ### Use `createChart` function to create a `dxchart5-react` component Applications that use `Vue` as a UI framework can easily use `createChart` function to render `ChartReactApp` in the usual `.vue` component. ```html ``` Container element of the `ChartReactApp` component should have the `height` and `width` in absolute units such as `px`, or `vh`, because that's how `CSS` works. So to make it display right, just give it a height `576px` and width `800px` for example. Then you should see the following. That's the bare minimum usage with mock data. ### Enjoy! ## How to use `ChartReactAPI` To call `ChartReactAPI` methods you should somehow store the link to the API object in your application. To do so in `Vue` app you can use `ref()` concept or assign API object reference to a plain variable, it depends on your application needs. > Full `ChartReactAPI` reference could be found on [API reference page](dxcharts-react/Quick-Start/Quick-Start---API). Here's a minimum example: ```html ``` ## How to configure `dxchart5-react` library `dxchart5-react` has a very strong configuration abilities, most of the time you would need to change it for your needs. > All the configurable stuff could be found on [configuration reference page](dxcharts-react/Quick-Start/Quick-Start---Configuration). ```html ``` ## Further reading - [[Quick Start - Configuration|Configuration]] - [[Quick Start - API|API]] - [[Quick Start - Providers|Providers]] - [[Quick Start - Appearance|Appearance]] --- ## dxcharts-react/Quick Start/Quick Start - Zip.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; import { MDXLink } from '../../../mdx-config/MDXLink'; import QuickStartZipCustomStyled from '!!raw-loader!./code/quick-start-zip-custom-styled.tsx'; # Quick Start - ZIP If you don't have an access to internal repository then this is a guide how to install `dxCharts` packages without it. ## Get ZIP `dxchart5-modules`, `dxchart5-react` and `dxstudies` packages most commonly are distributed via ZIP file (it could be trial version or some other special version, it doesn't matter). This is an approx content of a ZIP: - `/dxcharts_lib_5.5.0.zip` - `/dxchart5-modules` - `/dxchart5-react` - `/dxstudies` ## Setup your application There is a lot of ways how you can setup your application, to startup we recommend via `create-react-app` or `npm create vite`. > We highly recommend to use `typescript`, because `dxcharts` is fully typed, and it could be much easier to get into it. ## Install packages from ZIP via local packages feature `npm` and `yarn` have a mechanism to install local packages. Run these commands in your project root folder: ```bash # assuming ../dxchart.lib folder is UNZIPPED chart package npm install ../dxchart.lib/dxchart5-modules npm install ../dxchart.lib/dxstudies npm install ../dxchart.lib/dxchart5-react # or yarn@3.x.x (it's also available in the older versions, but the syntax may be different) yarn add ../dxchart.lib/dxchart5-modules yarn add ../dxchart.lib/dxstudies yarn add ../dxchart.lib/dxchart5-react ``` After those commands the packages should be installed locally. Your `package.json` file should look something like that: ```json { //... "dependencies": { "@dx-private/dxchart5-modules": "./dxchart.lib/dxchart5-modules", "@dx-private/dxchart5-react": "./dxchart.lib/dxchart5-react", "@dx-private/dxstudies": "./dxchart.lib/dxstudies" //... } } ``` `dxCharts` is a library so it has dependencies as well, including peer dependencies which should be installed. So you need to run: ```bash npm install # or yarn yarn install ``` ## Install `react` and `react-dom` `dxchart5-react` has `react` and `react-dom` packages as a `peerDependencies`, so to make library work you need to install those packages in your project, if you don't have them. > `dxchart5-react` library works only with `"react": "17.x.x"` and `"react-dom": "17.x.x"` or _higher_ ```bash npm install --save react react-dom # or yarn yarn add react react-dom ``` Congratulations! All the preparations are complete! Now you are ready to use `dxchart5-react` library. # How to use `dxchart5-react` library If you use `React` please follow the guide for [[Quick Start - React|React based applications]]. If your choice is `Vue` you can check the guide for [[Quick Start - Vue|Vue based applications]]. If you like `Angular` you can check the guide for [[Quick Start - Angular|Angular based applications]]. If you prefer `vanilla` or another not so widely used frameworks you'll find useful [[Quick Start - Vanilla|Vanilla guide]]. --- ## dxcharts-react/Quick Start/Quick Start.mdx import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; # Quick Start ## Install `dxCharts` is distributed via ZIP and private Nexus npm registry. Our product is not free (except `dxcharts-lite`) and there's two ways how you can have an access to it: - via ZIP file we provided you - via access to our private npm registry > If you have a ZIP file, please follow [[Quick Start - Zip|this tutorial]]. ## Setup your application There is a lot of ways how you can setup your application, to startup we recommend via `create-react-app` or `npm create vite`. > We highly recommend to use `typescript`, because `dxcharts` is fully typed, and it could be much easier to get into it. ## Install via Nexus npm registry First of all you need to login to Nexus: ```bash npm login --registry=https://repo.devexperts.com/repository/dxcharts/ ``` If you are an employee of `devexperts` you can use `devexperts` VPN and different Nexus npm registry for a quicker access: ```bash npm login --registry=https://nexus.in.devexperts.com/repository/npm-dx/ ``` If you're using **yarn version 3.x.x** then you would need to add the following into `.yarnrc.yml` file ```yaml yarnPath: .yarn/releases/yarn-3.6.0.cjs nodeLinker: node-modules npmScopes: dx-private: npmAlwaysAuth: true npmAuthToken: NpmToken.[your auth token here] npmRegistryServer: 'https://repo.devexperts.com/repository/dxcharts/' unsafeHttpWhitelist: - 'repo.devexperts.com' ``` ## Install `dxCharts` packages Install `dxCharts` packages that are required by following the steps below: ```bash npm install @devexperts/dxcharts-lite npm install @dx-private/dxchart5-modules npm install @dx-private/dxstudies npm install @dx-private/dxchart5-react # or yarn yarn add @devexperts/dxcharts-lite yarn add @dx-private/dxchart5-modules yarn add @dx-private/dxstudies yarn add @dx-private/dxchart5-react ``` Your `package.json` file should look something like that: ```json { "name": "your-package", "dependencies": { "@devexperts/dxcharts-lite": "2.7.13", "@dx-private/dxchart5-modules": "5.6.1", "@dx-private/dxchart5-react": "^5.6.1", "@dx-private/dxstudies": "52.0.0" //... } } ``` ## Install `react` and `react-dom` `dxchart5-react` has `react` and `react-dom` packages as a `peerDependencies`, so to make library work you need to install those packages in your project, if you don't have them. > `dxchart5-react` library works only with `"react": "17.x.x"` and `"react-dom": "17.x.x"` or _higher_ ```bash npm install --save react react-dom # or yarn yarn add react react-dom ``` Congratulations! All the preparations are complete! Now you are ready to use `dxchart5-react` library. # How to use `dxchart5-react` library If you use `React` please follow the guide for [[Quick Start - React|React based applications]]. If your choice is `Vue` you can check the guide for [[Quick Start - Vue|Vue based applications]]. If you like `Angular` you can check the guide for [[Quick Start - Angular|Angular based applications]]. If you prefer `vanilla` or another not so widely used frameworks you'll find useful [[Quick Start - Vanilla|Vanilla guide]]. --- ## dxcharts-react/Quick Start/code/quick-start-api-createChart.ts ```ts import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { createChart } from '@dx-private/dxchart5-react/dist/index'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; // append container somewhere in your app const container = document.createElement('dxcharts-container'); let chartReactAPI: ChartReactAPI | undefined = undefined; createChart(container, { dependencies: { ...CREATE_MOCK_PROVIDERS(), onApiCreated: api => (chartReactAPI = api), }, }); // append buttons somewhere you like in your app const changeInstrumentButton = document.createElement('button'); const changeAggregationButton = document.createElement('button'); const changeThemeButton = document.createElement('button'); changeInstrumentButton.addEventListener('click', () => { chartReactAPI?.changeInstrument('IBM'); }); changeAggregationButton.addEventListener('click', () => { chartReactAPI?.changePeriod({ duration: 1, durationType: 'h' }); }); changeThemeButton.addEventListener('click', () => { chartReactAPI?.changeTheme('light'); }); ``` --- ## dxcharts-react/Quick Start/code/quick-start-api-react.tsx ```tsx import React, { useCallback, useRef } from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export function App() { const chartReactAPI = useRef(); const onApiCreated = useCallback((api: ChartReactAPI) => { chartReactAPI.current = api; chartReactAPI.current.changeInstrument('IBM'); chartReactAPI.current.changePeriod({ duration: 1, durationType: 'h' }); chartReactAPI.current.changeTheme('light'); }, []); return (
); } ``` --- ## dxcharts-react/Quick Start/code/quick-start-configuration-createChart.ts ```ts import { createChart } from '@dx-private/dxchart5-react/dist/index'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; // append container somewhere in your app const container = document.createElement('dxcharts-container'); createChart(container, { dependencies: { ...CREATE_MOCK_PROVIDERS(), chartReactConfig: { drawings: { sidebar: { enabled: false, }, }, toolbar: { showButtonsTooltip: true, }, }, // The palette and colorPalette objects support all common color models: hex, rgb/rgba, hsl/hsla, default strings ('green', 'yellow', etc...), // but the most native for chart is rgba, so it's recommended to use it if possible colorPalette: [ 'rgb(255,255,0)', 'rgba(0,0,0,1)', '#ad00fa', 'green', 'hsl(0, 100%, 50%)', 'hsla(0, 100%, 64%, 0.2)', ], initialChartConfig: { components: { crossTool: { // crosstool supports three types: 'cross-and-labels', 'only-labels' and 'none' type: 'cross-and-labels', }, }, }, }, }); ``` --- ## dxcharts-react/Quick Start/code/quick-start-configuration-demo.tsx ```tsx import React from 'react'; import { ChartReactAppWrapper } from '../../../../components/utils/ChartReactApp'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; export const ChartConfigurationDemo = () => { return ( ); }; ``` --- ## dxcharts-react/Quick Start/code/quick-start-configuration-react.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export function App() { return (
); } ``` --- ## dxcharts-react/Quick Start/code/quick-start-providers-react.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const ChartReactAppWithMockProviders = () => { return (
); }; ``` --- ## dxcharts-react/Quick Start/code/quick-start-react.tsx ```tsx import React from 'react'; import { createRoot } from 'react-dom/client'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; function App() { return (
); } const rootEl = document.getElementById('root'); if (rootEl) { const root = createRoot(rootEl); root.render(); } ``` --- ## dxcharts-react/Quick Start/code/quick-start-zip-custom-styled.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const App = () => { return (
); }; export default App; ``` --- ## dxcharts-react/Studies.mdx import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; import DxStudiesProvider from '!!raw-loader!../../../../chart-react/src/providers/studies/dx-studies-provider.ts'; import studiesList from '!!raw-loader!../../../../chart-react/src/config/studies-list.ts'; import { MDXLink } from '../../mdx-config/MDXLink'; import customListOfStudies from './img/customListOfStudies.png'; import { DefaultStudiesList } from './code/studies.tsx'; import providerWithCustomStudiesList from '!!raw-loader!./code/studies-custom-list.tsx'; # Studies Indicator-based analysis is a technique widely used by traders to assist them in making decisions about which trades to execute and where to enter and exit them. This documentation provides information on setting up and creating studies within an application. ## DxStudiesProvider The `DxStudiesProvider` is responsible for prodiving data for studies, list of available studies and settings for them. > By default `DxStudiesProvider` is automatically provided, so there's no need to pass it manually. If you want a `custom` solution, you can create your own custom `DxStudiesProvider` which implementation should satisfy our interface: ## Configuring list of studies Configuring a list of available studies is essential if you are utilizing `dxCharts` studies and do not wish to develop `DxStudiesProvider` from scratch. Here is full list of available studies: To incorporate your preferred list of studies, you must first import `DEFAULT_STUDIES_LIST` and `fromRawStudiesSettings` functions. For example, let's say you would like to include only those three studies: **Aroon**, **SROC**, and **MACD**. {providerWithCustomStudiesList} After that you can see a list of 3 available studies: **Aroon**, **SROC**, and **MACD** --- ## dxcharts-react/Styling.mdx import { StyledChartReactApp } from './code/Styling.tsx'; import { FlexContainer } from '../../components/ui/FlexContainer.tsx'; import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock.tsx'; import App from '!!raw-loader!./code/App.tsx'; import ChartStyled from '!!raw-loader!./code/styles/chart.styled.ts'; import CheckboxStyled from '!!raw-loader!./code/styles/checkbox.styled.ts'; import SelectboxStyled from '!!raw-loader!./code/styles/selectbox.styled.ts'; import StudiesSettingsPopupStyled from '!!raw-loader!./code/styles/studies-settings-popup.styled.ts'; import ChartReactAppStyled from '!!raw-loader!./code/styles/chart-react-app.styled'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration.tsx'; import code from '!!raw-loader!./code/Styling.tsx'; import { MDXLink } from '../../mdx-config/MDXLink.tsx'; # Styling In `chart-react` we are using the `styled-components` library under the hood, so because of this, `chart-react` has some options to override default components styles. Let's break it down! ## Styled-Components Cascades In a `styled-components` there is a thing like referring to other components inside interpolation function. So you can use it without a limit to apply custom styles for every styled component, no matter how deep in the structure it is located. Consider we've created a wrapper `ChartReactApp` component around our `chart-react` app, which only wires all necessary dependencies. To style everything underneath it, we should wrap our `ChartReactApp` inside a `styled`, and write styles here. Let's change styles for the `Studies Settings` popup component, for it we should do the following: {ChartStyled} Someday your styles will end up with a lot lines of code. But we can split it to different files: {StudiesSettingsPopupStyled} # {ChartReactAppStyled} ## Overriding Styles For a General UI-Components To overwrite the general UI components styles in the whole app we can do something like: {CheckboxStyled} # {SelectboxStyled} And then use it like before, in the root of `ChartReactAppStyled`: ### Styling Popover and Popup Components Popover and Popup components from the chart-kit have some limitations in styling. Because they are appended to the `
` in the `DOM`, they can only be styled from the element that is upper in the `DOM` tree then that `div`, like we have done previously with the `` example. # Example Source code: {code} ## Hiding components You can use css classes to hide unused elements - either statically with CSS or dynamically with JavaScript. For example, this approach works well for components such as right click menus ``` .BackgroundMenu { display: none; } ``` --- ## dxcharts-react/Timezones.mdx import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration'; import TimezonesConfig from '!!raw-loader!../../../../chart-react/src/utils/timezones/timezone.model.ts'; import { MDXCodeBlock } from '../../mdx-config/code-block/MDXCodeBlock'; import TimezonesCode from '!!raw-loader!./code/timezones.tsx'; import hourUtc from './img/hourUtc.png'; import hourOffset from './img/hourOffset.png'; import dayUtc from './img/dayUtc.png'; import dayOffset from './img/dayOffset.png'; # Timezones in Chart-React ## UTC UTC option represent the UTC timezone or 00:00. ## Exchange Exchange option represent the timezone in which the instrument is traded. Exchange is dynamically updated based on the trading hours of the instrument. If the trading hours of the instrument are for example: {`NewYorkUS(rt=0300;0=p04000930r09301600a16002000)`} The result would be the America/New_York timezone, which is the New York timezone with an offset -04:00, or UTC-4. ## Configuration Any timezone identifier is stored in a `currentTimezone` config. Possible values: `UTC` for utc, `Exchange` for exchange or any other [Timezones database value](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List), for example, `Asia/Bangkok`: ```ts timezones: { currentTimezone: 'UTC' }, ``` {TimezonesCode} > UTC and Exchange are predefined and you cannot exclude them from the list. If you wish to have only them in the list of timezones then pass empty array to the listOfTimezones configuration: ```ts timezones: { currentTimezone: 'UTC', listOfTimezones: [] } ``` If you don't want to see timezone controls at all you can disable it via dependencies: ```ts chartReactConfig: { timezoneControls: { enabled: false; } } ``` ## Timezones and candles timestamps > To correctly display candles timestamps information on chart please make sure the source of data and [[dxcharts-react/API-Reference---Providers|CandleDataProvider]] send the candles timestamps which correspond to UTC timezone, or 00:00 day time. **Explanation**: timezone model has a `currentTzOffset` function, which allows to apply the selected timezone's offset to the candles timestamp to correctly show the data according to the selected timezone. If the period is less than a day, the **offset applies** to the UTC timestamp:
On the other hand, if the period is a day or more, the offset is not applied, because it's not needed anymore since candles have at least a day difference inbetween them, so candles always have UTC timezone (00:00 format):
## Further reading [[Timezones - Core|Timezones in dxcharts-lite]] [[API Reference - Providers|Trading sessions provider]] --- ## dxcharts-react/Trading.mdx import { TradingConfiguration } from './code/trading-from-chart.tsx'; import { MDXCodeBlockDeclaration } from '../../mdx-config/code-block/MDXCodeBlockDeclaration.tsx'; import { DedokMethods } from '../../components/dedok/DedokMethods/DedokMethods.tsx'; import PartialTradingConfig from '!!raw-loader!../../../../chart-react/src/config/chart-react-config.ts'; import OrderModel from '!!raw-loader!../../../../chart-react/src/chart/model/trading/order.model.ts'; import PositionModel from '!!raw-loader!../../../../chart-react/src/chart/model/trading/position.model.ts'; import protectionOrdersImg from './img/protectionOrders.png'; # Trading ## Supplying orders & positions to chart You have to implement these 2 providers in order to show orders and positions on chart: ## Configuration For "OCO" orders > OCO - One Cancels Other. It's just two linked orders which are created simultaneously and if one of them is executed - other is cancelled automatically. Right now OCO orders is not fully supported by chart, which means that there is no UI for issuing OCO orders. But you can use your own UI to issue OCO orders. You just need to specify `canCreateOCO: true` property in model for order or position which are supplied by your providers. ## Configuration For "Stop Loss" and "Take Profit" orders Firstly, you should implement `positionProvider` and `orderProvider` on its side. Orders and positions have the ability to create protection orders (stop loss and take profit). After the order is created on the UI and the price is selected, the `createOrder` function of the provider is called, which contains the newly created protection order with the following information: Take a look at protection order example: ```javascript { id: "protection_order_1", limitPrice: 229.2863986896831, orderType: "limit", originalItemId: "order_1", quantity: 100, side: "sell", type: "tp" } ``` Each protection order has connection with it's parent - original order, by `originalItemId` field. Similarly, original order has field `protectionOrderIds`. Take a look: ```javascript { id : "order_1", limitPrice : 225.0514097391597 , orderType : "limit", quantity : 100, side : "buy", stopPrice : 225.0514097391597, type : "original", protectionOrderIds: [undefined, "protection_order_1"]; } ``` You should store this order on your side in the storage (at the your discretion), and can change it as convenient. > Also, you should remember, that if you delete protection order, you should also remove connection from original order by editing `protectionOrderIds` field. And if you delete original order, don't forget to remove protection orders instances from your store. The same behavior is supported for Positions. After changing the order, you need to send all orders to the `observeOrders` method, and they will be displayed on the chart with new data If you correctly applied rules above, you will see the following: ## Configuration For "Trading" You can configure trading using the following interface from `ChartReactConfig#trading` --- ## dxcharts-react/Watermark/Overview.mdx import { MDXCodeBlockDeclaration } from '../../../mdx-config/code-block/MDXCodeBlockDeclaration'; import code from '!!raw-loader!./WaterMarkExample'; import { WaterMarkChartExample } from './WaterMarkExample'; # Watermark ## Watermark customization [ChartReactConfig](dxcharts-react/api-reference-configuration) contains object called `watermark`, and the property `mode` in it allows to choose between default chart watermark logic and your own. Initially, the `instrument` mode is selected, which makes watermark data impossible to be changed, because inner logic is based on the selected instrument and displays information about it. If you want to change it, the `custom` mode should be applied to the watermark config After that, watermark data could be changed as you like. In the example below watermark shows the selected chart type: ## Further reading - [[WaterMarkComponent|Watermark Component API]] - [Watermark in dxcharts-lite](dxcharts-lite/Watermark) --- ## dxcharts-react/Watermark/WaterMarkExample.tsx ```tsx import React from 'react'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; export const WaterMarkChartExample = () => { const onApiCreated = (api: ChartReactAPI) => { api.onChartCreated((chartId, chartInstance) => { const waterMarkComponent = chartInstance.watermark; // initial set waterMarkComponent.setWaterMarkData({ firstRow: chartInstance.config.components.chart.type }); // update when chart type changes chartInstance.chartModel.chartTypeChanged.subscribe(chartType => { waterMarkComponent.setWaterMarkData({ firstRow: chartType }); }); }); }; return (
); }; ``` --- ## dxcharts-react/code/App.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import styled from 'styled-components'; import { StyledCheckbox } from './styles/checkbox.styled'; import { StyledSelectbox } from './styles/selectbox.styled'; export const InitChartReactAppStyled = () => { const ChartReactAppStyled = styled(ChartReactApp)``; return ; }; export const ChartReactAppStyled = () => { const ChartReactAppStyled = styled(ChartReactApp)` /* ui-components styles overridings */ ${() => StyledCheckbox} ${() => StyledSelectbox} `; return ChartReactAppStyled; }; ``` --- ## dxcharts-react/code/KeyboardShortcuts.styled.ts ```ts import styled from 'styled-components'; export const KeyboardShortcutsTable = styled.table` border-spacing: 0; text-align: left; `; export const KeyboardShortcutsTableHead = styled.thead``; export const KeyboardShortcutsTableHeadRow = styled.tr``; export const KeyboardShortcutsHeadCell = styled.th` padding-top: var(--spacer-4); padding-bottom: var(--spacer-4); padding-left: 15px; min-width: 200px; `; export const KeyboardShortcutsTableBody = styled.tbody``; export const KeyboardShortcutsTableBodyRow = styled.tr` background: var(--left-sidebar-bg-color); &:last-of-type, &:first-of-type { border-radius: 8px; } `; export const KeyboardShortcutsCell = styled.td` border-top: 1px solid rgba(255, 255, 255); padding: 30px 15px; min-width: 200px; max-width: 100%; vertical-align: middle; `; export const KeyboardShortcutsCellShortcutText = styled.span` width: max-content; padding: var(--spacer-1); border: 1px solid rgba(255, 255, 255); border-radius: 4px; margin: 0; margin-right: var(--spacer-2); `; export const KeyboardShortcutsCellDescriptionText = styled.p` margin: 0; `; ``` --- ## dxcharts-react/code/KeyboardShortcuts.tsx ```tsx import React from 'react'; import { KeyboardShortcutsTable, KeyboardShortcutsTableHead, KeyboardShortcutsTableHeadRow, KeyboardShortcutsHeadCell, KeyboardShortcutsTableBody, KeyboardShortcutsTableBodyRow, KeyboardShortcutsCell, KeyboardShortcutsCellShortcutText, KeyboardShortcutsCellDescriptionText, } from './KeyboardShortcuts.styled'; interface KeyboardShortcutsContent { shortcuts: string[]; description: string; } export const KeyboardShortcuts = () => { return ( Shortcut Description {keyboardShortcutsContent.map(item => ( {item.shortcuts.map(i => ( {i} ))} {item.description} ))} ); }; const keyboardShortcutsContent: KeyboardShortcutsContent[] = [ { shortcuts: ['Ctrl+Z', '⌘+Z'], description: 'Undo Action', }, { shortcuts: ['Ctrl+Shift+Z', '⌘+Shift+Z'], description: 'Redo Action' }, { shortcuts: ['Ctrl+E', '⌘+E'], description: 'Rename layout / Rename indicator template' }, { shortcuts: ['Ctrl+O', '⌘+O'], description: 'Open dxScript editor window' }, { shortcuts: ['←', '↑', '→', '↓'], description: 'Navigate between list items', }, { shortcuts: ['Delete'], description: `Delete layout / indicator template / drawing / study from settings`, }, { shortcuts: ['Backspace'], description: 'Delete drawing' }, { shortcuts: ['Enter', 'Space'], description: 'Select any clickable element (buttons, inputs, etc...), commit a drawing', }, { shortcuts: ['Esc'], description: 'Cancel drawing, close popover' }, { shortcuts: ['Shift'], description: 'Hold to enable magnet mode for a selected drawing', }, ]; ``` --- ## dxcharts-react/code/Styling.tsx ```tsx import React from 'react'; import styled, { css } from 'styled-components'; import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp'; import { StudiesSettingsPopupStyled, FooterButtonStyled, } from '@dx-private/dxchart5-react/dist/chart/components/studies/studies-settings/studies-settings-popup.styled'; import { PopupFooterStyled, PopupContainerStyled, PopupHeaderStyled, } from '@dx-private/dxchart5-react/dist/chart-kit/Popup/PopupUI.styled'; import { IndicatorContainerStyled, IndicatorTitleStyled, } from '@dx-private/dxchart5-react/dist/chart/components/studies/studies-settings/components/indicator-list/indicator-list-section.styled'; import { IndicatorListItemContainerStyled } from '@dx-private/dxchart5-react/dist/chart/components/studies/studies-settings/components/indicator-list/indicator-list-item.styled'; import { LeftSectionStyled, RightSectionStyled, DeleteButtonStyled, } from '@dx-private/dxchart5-react/dist/chart/components/studies/studies-settings/components/studies-settings-content/studies-settings-content.styled'; import { StudySettingsTitleStyled, RestoreToDefaultButtonStyled, InputsBlockTitleStyled, LabeledFormFieldLabelStyled, SelectBoxItemStyled, } from '@dx-private/dxchart5-react/dist/chart/components/studies/studies-settings/components/study-settings/study-settings.styled'; import { CheckboxContainerStyled } from '@dx-private/dxchart5-react/dist/chart-kit/Checkbox/Checkbox.styled'; import { CheckboxIconStyled, CheckboxViewStyled, CheckboxInputStyled, } from '@dx-private/dxchart5-react/dist/chart-kit/Checkbox/Checkbox.styled'; import { SelectboxAnchorStyled, SelectboxAnchorCaretStyled, SelectboxAnchorContentStyled, } from '@dx-private/dxchart5-react/dist/chart-kit/Selectbox/SelectboxAnchor.styled'; import { PopoverStyled, PopoverContentStyled } from '@dx-private/dxchart5-react/dist/chart-kit/Popover/Popover.styled'; import { DropdownMenuItemStyled } from '@dx-private/dxchart5-react/dist/chart-kit/Menu/dropdown-menu/DropdownMenuItem.styled'; import { MenuStyled } from '@dx-private/dxchart5-react/dist/chart-kit/Menu/Menu.styled'; import { CompareChartNoDataContentStyled } from '@dx-private/dxchart5-react/dist/chart/components/compare-chart/compare-chart-selector.styled'; import { MultichartSettingsLayoutSelectorItemStyled, MultichartSettingsHeaderStyled, MultichartSettingsOptionTextStyled, } from '@dx-private/dxchart5-react/dist/chart/components/multichart-settings/multichart-settings.styled'; import { DrawingSelectorFooterButtonStyled, DrawingSelectorFooterStyled, } from '@dx-private/dxchart5-react/dist/chart/components/drawings/drawings-selector/drawings-selector-footer.styled'; export const StyledChartReactApp = () => { return ; }; const ChartReactAppWrapperStyled = styled(ChartReactAppWrapper)` /* Popups overridings */ ${() => StyledStudiesSettingsPopup} /* ui-components styles overridings */ ${() => StyledCheckbox} ${() => StyledSelectbox} ${() => StyledPopover} /* Popovers Concrete popovers overridings */ ${() => StyledStudySettingsPopover} ${() => StyledDrawingsSelectorPopover} ${() => StyledCompareChartPopover} ${() => StyledMultichartPopover} `; const StyledStudiesSettingsPopup = css` ${StudiesSettingsPopupStyled} { ${() => StyledStudiesSettingsLeftSection} ${() => StyledStudiesSettingsRightSection} ${PopupHeaderStyled} { background-color: #717744; color: #eff1ed; border-top-left-radius: 0; border-top-right-radius: 0; } ${PopupContainerStyled} { background-color: #bcbd8b; border: 2px solid #766153; border-radius: 0; } ${PopupFooterStyled} { background-color: #717744; border-bottom-left-radius: 0; border-bottom-right-radius: 0; justify-content: flex-start; padding: 10px 10px; } ${FooterButtonStyled} { background-color: #766153; color: #eff1ed; &:hover { background-color: #373d20; } } } `; const StyledStudiesSettingsLeftSection = css` ${LeftSectionStyled} { ${IndicatorContainerStyled} { border-right: 1px solid #766153; } ${IndicatorListItemContainerStyled} { color: #eff1ed; &:hover { background-color: #766153; } } ${IndicatorTitleStyled} { color: #373d20; } } `; const StyledStudiesSettingsRightSection = css` ${RightSectionStyled} { ${() => StyledStudySettings}; ${IndicatorTitleStyled} { color: #373d20; } ${DeleteButtonStyled} { color: #766153; &:hover { color: #373d20; } } } `; const StyledStudySettings = css` ${() => StyledStudySettingsInputsBlock}; ${StudySettingsTitleStyled} { color: #373d20; } ${RestoreToDefaultButtonStyled} { color: #766153; &:hover { color: #373d20; } } `; const StyledStudySettingsInputsBlock = css` ${InputsBlockTitleStyled} { color: #766153; } ${LabeledFormFieldLabelStyled} { color: #eff1ed; } `; const StyledCheckbox = css` ${CheckboxContainerStyled} { ${CheckboxIconStyled} { fill: #eff1ed; } ${CheckboxViewStyled} { border-color: #eff1ed; } ${CheckboxInputStyled}:hover + ${CheckboxViewStyled} { border-color: #766153; } ${CheckboxInputStyled}:active + ${CheckboxViewStyled} { border-color: #766153; } } `; const StyledSelectbox = css` ${SelectboxAnchorStyled} { background-color: #717744; border-radius: 0; &:hover { background-color: #766153; } } ${SelectboxAnchorCaretStyled} { color: #eff1ed; } ${SelectboxAnchorContentStyled} { color: #eff1ed; } `; const StyledPopover = css` ${PopoverStyled} { border-radius: 0; background-color: #717744; border: 2px solid #766153; ${PopoverContentStyled}:not(:empty) { background-color: #717744; } ${MenuStyled} { padding: 0; } ${DropdownMenuItemStyled} { background-color: #717744; margin: 0; &:hover { border-radius: 0; background-color: #766153; } } } `; const StyledStudySettingsPopover = css` ${PopoverStyled} { ${SelectBoxItemStyled} { &:hover { border-radius: 0; background-color: #bcbd8b; } } } `; const StyledDrawingsSelectorPopover = css` ${PopoverStyled} { ${DrawingSelectorFooterStyled} { &::after { background-color: #eff1ed; } } ${DrawingSelectorFooterButtonStyled} { color: #eff1ed; &:hover { border-radius: 0; background-color: #766153; } &:disabled, &[disabled] { cursor: not-allowed; } } } `; const StyledCompareChartPopover = css` ${CompareChartNoDataContentStyled} { color: #eff1ed; } `; const StyledMultichartPopover = css` ${MultichartSettingsLayoutSelectorItemStyled} { background-color: #bcbd8b; } ${MultichartSettingsHeaderStyled} { color: #eff1ed; } ${MultichartSettingsOptionTextStyled} { color: #eff1ed; } `; ``` --- ## dxcharts-react/code/bundling.example.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const ChartReactWithDisabledWorkers = () => { return ( ); }; ``` --- ## dxcharts-react/code/chart-react-on-api-created.tsx ```tsx import React from 'react'; import { ChartReactAPI } from '@dx-private/dxchart5-react/dist/chart/view-models/api/chart-react-api.view-model'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const createChartReactApp = () => { const onApiCreated = (api: ChartReactAPI) => { // eslint-disable-next-line deprecation/deprecation api.changeInstrument('IBM'); api.changePeriod({ duration: 1, durationType: 'h' }); api.changeTheme('light'); // ... api.internal.multiChartViewModel.setLayout('1x2'); }; return ; }; ``` --- ## dxcharts-react/code/chart-react.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const createChartReactApp = () => { const dependencies = { ...CREATE_MOCK_PROVIDERS(), //... }; return ; }; ``` --- ## dxcharts-react/code/date-format-override.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; export const ChartApp = () => { 'MM.dd.yyyy', events: () => 'MM.dd.yyyy', dateInput: 'MDY', }, }, }} />; }; export const ChartApp_2 = () => { "12:46:33", // this is an example, and you can write here your own implementation createFormatterFunction: (pattern: string) => (dateTimestamp: number) => { const date = new Date(dateTimestamp); const hour = date.getHours(); const minute = date.getMinutes(); const seconds = date.getSeconds(); const day = date.getDay(); const month = date.getMonth(); const year = date.getFullYear(); const formattedDate = pattern .replace('yyyy', `${year}`) .replace('yy', `${year}`) .replace('MM', month < 10 ? `☢0${month}` : `☢${month}`) .replace('MMM', month < 10 ? `☢0${month}` : `☢${month}`) .replace('dd', day < 10 ? `↗0${day}` : `↗${day}`) .replace('HH', hour < 10 ? `☀0${hour}` : `☀${hour}`) .replace('mm', minute < 10 ? `☔0${minute}` : `☔${minute}`) .replace('s', seconds < 10 ? `☘0${seconds}` : `☘${seconds}`); return formattedDate; }, }, }, }} />; }; ``` --- ## dxcharts-react/code/loading-state.tsx ```tsx import React, { useMemo } from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { AggregationPeriod } from '@dx-private/dxchart5-react/dist/chart/model/aggregation.model'; import { ChartReactAppContainerStyled } from '../../../components/chart-theme-builder/chart-theme-builder.styled'; import { InitialLoadingItem } from '@dx-private/dxchart5-react/dist/chart/view-models/loading/initial-loader.vm'; import { ChartCandleData } from '@dx-private/dxchart5-react/dist/providers/chart-data-provider'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; export const ChartReactAppWithLoadingState = () => { const MOCK_PROVIDERS = CREATE_MOCK_PROVIDERS(); const initialInstrument = 'AAPL'; const initialPeriod: AggregationPeriod = useMemo(() => ({ duration: 1, durationType: 'h' }), []); const candlesLoadedPromise: Promise = useMemo( () => MOCK_PROVIDERS.chartDataProvider.requestHistoryData(initialInstrument, initialPeriod), [MOCK_PROVIDERS.chartDataProvider, initialPeriod], ); const initialLoading: InitialLoadingItem[] = [ { itemName: 'candlesLoaded', item: candlesLoadedPromise, itemWeight: 1 }, ]; return ( ); }; ``` --- ## dxcharts-react/code/localization.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { DEFAULT_LOCALIZATION, Localization } from '@dx-private/dxchart5-react/dist/config/localization/localization'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; const userLocalization: Localization = { ...DEFAULT_LOCALIZATION, sidebar: { ...DEFAULT_LOCALIZATION.sidebar, header: 'Drawings', buttons: { magnetMode: 'Magnet Mode', drawingMode: 'Drawing Mode', syncDrawings: 'Synchronize drawings', hideDrawings: 'Hide all drawings', showDrawings: 'Unhide all drawings', deleteDrawings: 'Delete all drawings', }, }, //...overrides, }; export const UserApp = () => { return ( ); }; ``` --- ## dxcharts-react/code/navigationMap.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers'; export const NavigationMapExample = () => { return (
); }; ``` --- ## dxcharts-react/code/studies-custom-list.tsx ```tsx import React from 'react'; import { DEFAULT_STUDIES_LIST } from '@dx-private/dxchart5-react/dist/config/studies-list'; import { fromRawStudiesSettings } from '@dx-private/dxchart5-react/dist/chart/model/studies.model'; import { createDxStudiesProvider } from '@dx-private/dxchart5-react/dist/providers/studies/dx-studies-provider'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; // list of studies you would like to see const listOfStudies = ['Aroon', 'SROC', 'MACD']; export const dxStudiesProviderWithCustomList = createDxStudiesProvider( // here in `DEFAULT_STUDIES_LIST` function you can pass your localization for studies as an argument DEFAULT_STUDIES_LIST() .filter(study => listOfStudies.includes(study.id)) .map(fromRawStudiesSettings), ); export const dxChartsWithCustomStudiesList = () => { const deps = { ...CREATE_MOCK_PROVIDERS(), // ...otherDependencies, // wire up your custom dxStudesProvider dxStudiesProvider: dxStudiesProviderWithCustomList, }; return ; }; ``` --- ## dxcharts-react/code/studies.tsx ```tsx import React, { useMemo } from 'react'; import { DEFAULT_STUDIES_IDS } from '@dx-private/dxchart5-react/dist/config/studies-list'; import { MDXCodeBlock } from '../../../mdx-config/code-block/MDXCodeBlock'; export const DefaultStudiesList = () => { const studiesListString = useMemo(() => { return `const DEFAULT_STUDIES_LIST = [\n${Object.values(DEFAULT_STUDIES_IDS) .map(name => ` "${name}",\n`) .join('')}];`; }, []); return {studiesListString}; }; ``` --- ## dxcharts-react/code/styles/chart-react-app.styled.ts ```ts import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import styled from 'styled-components'; import { StudiesSettingsStyled } from './studies-settings-popup.styled'; // ${() => StudiesSettingsStyled} is a way to interpolate the styles from the css`` function; export const ChartReactAppStyled = styled(ChartReactApp)` ${() => StudiesSettingsStyled} `; ``` --- ## dxcharts-react/code/styles/chart.styled.ts ```ts import { PopupContainerStyled, PopupFooterStyled, PopupHeaderStyled, } from '@dx-private/dxchart5-react/dist/chart-kit/Popup/PopupUI.styled'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { FooterButtonStyled, StudiesSettingsPopupStyled, } from '@dx-private/dxchart5-react/dist/chart/components/studies/studies-settings/studies-settings-popup.styled'; import styled from 'styled-components'; /** * StudiesSettingsPopupStyled and etc are components itself * which you should import to use as a class selector */ // Under the hood styled-components simply interpolates these components to the associated classnames export const ChartReactAppStyled = styled(ChartReactApp)` ${StudiesSettingsPopupStyled} { ${PopupHeaderStyled} { background-color: #717744; color: #eff1ed; border-top-left-radius: 0; border-top-right-radius: 0; } ${PopupContainerStyled} { background-color: #bcbd8b; border: 2px solid #766153; border-radius: 0; } ${PopupFooterStyled} { background-color: #717744; border-bottom-left-radius: 0; border-bottom-right-radius: 0; justify-content: flex-start; padding: 10px 10px; } ${FooterButtonStyled} { background-color: #766153; color: #eff1ed; &:hover { background-color: #373d20; } } } `; ``` --- ## dxcharts-react/code/styles/checkbox.styled.ts ```ts // checkbox.styled.ts import { CheckboxIconStyled, CheckboxInputStyled, CheckboxViewStyled, } from '@dx-private/dxchart5-react/dist/chart-kit/Checkbox/Checkbox.styled'; import { css } from 'styled-components'; export const StyledCheckbox = css` ${CheckboxIconStyled} { fill: #eff1ed; } ${CheckboxViewStyled} { border-color: #eff1ed; } ${CheckboxInputStyled}:hover + ${CheckboxViewStyled} { border-color: #766153; } ${CheckboxInputStyled}:active + ${CheckboxViewStyled} { border-color: #766153; } `; ``` --- ## dxcharts-react/code/styles/selectbox.styled.ts ```ts // selectbox.styled.ts import { SelectboxAnchorCaretStyled, SelectboxAnchorContentStyled, SelectboxAnchorStyled, } from '@dx-private/dxchart5-react/dist/chart-kit/Selectbox/SelectboxAnchor.styled'; import { css } from 'styled-components'; export const StyledSelectbox = css` ${SelectboxAnchorStyled} { background-color: #717744; border-radius: 0; &:hover { background-color: #766153; } } ${SelectboxAnchorCaretStyled} { color: #eff1ed; } ${SelectboxAnchorContentStyled} { color: #eff1ed; } `; ``` --- ## dxcharts-react/code/styles/studies-settings-popup.styled.ts ```ts // studies-settings-popup.styled.ts import { PopupContainerStyled, PopupFooterStyled, PopupHeaderStyled, } from '@dx-private/dxchart5-react/dist/chart-kit/Popup/PopupUI.styled'; import { FooterButtonStyled, StudiesSettingsPopupStyled, } from '@dx-private/dxchart5-react/dist/chart/components/studies/studies-settings/studies-settings-popup.styled'; import { css } from 'styled-components'; export const StudiesSettingsStyled = css` ${StudiesSettingsPopupStyled} { ${PopupHeaderStyled} { background-color: #717744; color: #eff1ed; border-top-left-radius: 0; border-top-right-radius: 0; } ${PopupContainerStyled} { background-color: #bcbd8b; border: 2px solid #766153; border-radius: 0; } ${PopupFooterStyled} { background-color: #717744; border-bottom-left-radius: 0; border-bottom-right-radius: 0; justify-content: flex-start; padding: 10px 10px; } ${FooterButtonStyled} { background-color: #766153; color: #eff1ed; &:hover { background-color: #373d20; } } } `; ``` --- ## dxcharts-react/code/timezones.tsx ```tsx import React from 'react'; import { ChartReactApp } from '@dx-private/dxchart5-react/dist/chart/chart-react-app'; import { CREATE_MOCK_PROVIDERS } from '@dx-private/dxchart5-react-mock-providers/dist'; const timezones = { currentTimezone: 'Pacific/Fiji', // valid formats of timezones listOfTimezones: ['America/Argentina/Buenos_Aires', 'Asia/Bangkok', 'CET', 'CST6CDT', 'Etc/GMT+8', 'Etc/UTC'], }; export const YourApp = () => { return ( ); }; ``` --- ## dxcharts-react/code/trading-from-chart.tsx ```tsx import React, { useMemo, useState } from 'react'; import { FlexContainer } from '../../../components/ui/FlexContainer'; import { ChartReactAppWrapper } from '../../../components/utils/ChartReactApp'; import { ChartWidgetConstructorCheckboxContainerStyled, ChartWidgetConstructorCheckboxStyled, ChartWidgetConstructorCheckboxesStyled, ChartWidgetConstructorInputContainerStyled, ChartWidgetConstructorInputStyled, ChartWidgetConstructorInputsStyled, ChartWidgetConstructorLabelTextStyled, } from '../../dxcharts-react-widget/chart-widget-constructor/chart-widget-constructor.styled'; export const TradingConfiguration = () => { const [enabled, setEnabled] = useState(true); const [defaultOrderQuantity, setDefaultOrderQuantity] = useState(100); const [maxOrderQuantity, setLimitOrderQuantity] = useState(1000); const [currency, setCurrency] = useState('$'); const config = useMemo(() => { return { trading: { enabled, takeProfitStopLossEnabled: true, defaultOrderQuantity, maxOrderQuantity, currency, addNewOrderEnabled: true, showPriceAsLabels: false, rightOffset: 40, orderTypes: { market: true, limit: true, stop: true, }, }, }; }, [currency, defaultOrderQuantity, maxOrderQuantity, enabled]); return (
setEnabled(v)} /> {'Trading enabled'} {'Order quantity'} v !== undefined && setDefaultOrderQuantity(parseInt(v, 10) || 0)} /> {'Currency'} v !== undefined && setCurrency(v)} /> {'Limit order quantity'} v !== undefined && setLimitOrderQuantity(parseInt(v, 10))} />
); }; ``` --- ## dxcharts-react-widget/Widget-6-charts.mdx --- tags: widget --- import { SeveralWidgets } from './severalWidgets/SeveralWidgets.tsx'; # Six charts widget --- ## dxcharts-react-widget/Widget - IFrame.mdx --- tags: widget --- import { ChartWidgetIframe } from './iframe/chart-widget-iframe.tsx'; # Widget inside Iframe Copy-paste `