├── .editorconfig ├── .eslintignore ├── .eslintrc.yaml ├── .gitattributes ├── .github ├── .dependabot.yml ├── codeql-config.yml ├── issue_template.md ├── lock.yml ├── pull_request_template.md ├── stale.yml └── workflows │ ├── ci.yml │ ├── codeql-analysis.yml │ ├── gh-pages.yml │ └── npm.yml ├── .gitignore ├── .prettierrc.yaml ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── annotations ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── Annotate.tsx │ ├── BarAnnotation.tsx │ ├── Label.tsx │ ├── LabelAnnotation.tsx │ ├── SvgPathAnnotation.tsx │ └── index.ts └── tsconfig.json ├── axes ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── Axis.tsx │ ├── AxisZoomCapture.tsx │ ├── XAxis.tsx │ ├── YAxis.tsx │ └── index.ts └── tsconfig.json ├── charts ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── coordinates ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── CrossHairCursor.tsx │ ├── CurrentCoordinate.tsx │ ├── Cursor.tsx │ ├── EdgeCoordinate.tsx │ ├── EdgeCoordinateV2.tsx │ ├── EdgeCoordinateV3.tsx │ ├── EdgeIndicator.tsx │ ├── MouseCoordinateX.tsx │ ├── MouseCoordinateXV2.tsx │ ├── MouseCoordinateY.tsx │ ├── PriceCoordinate.tsx │ └── index.ts └── tsconfig.json ├── core ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── CanvasContainer.tsx │ ├── Chart.tsx │ ├── ChartCanvas.tsx │ ├── EventCapture.tsx │ ├── GenericChartComponent.tsx │ ├── GenericComponent.tsx │ ├── MoreProps.ts │ ├── index.ts │ ├── useEvent.ts │ ├── utils │ │ ├── ChartDataUtil.ts │ │ ├── PureComponent.tsx │ │ ├── accumulatingWindow.ts │ │ ├── barWidth.ts │ │ ├── closestItem.ts │ │ ├── evaluator.ts │ │ ├── identity.ts │ │ ├── index.ts │ │ ├── noop.ts │ │ ├── shallowEqual.ts │ │ ├── slidingWindow.ts │ │ ├── strokeDasharray.ts │ │ └── zipper.ts │ └── zoom │ │ ├── index.ts │ │ └── zoomBehavior.ts └── tsconfig.json ├── indicators ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── calculator │ │ ├── atr.ts │ │ ├── bollingerband.ts │ │ ├── change.ts │ │ ├── compare.ts │ │ ├── defaultOptionsForComputation.ts │ │ ├── elderRay.ts │ │ ├── ema.ts │ │ ├── forceIndex.ts │ │ ├── heikinAshi.ts │ │ ├── index.ts │ │ ├── kagi.ts │ │ ├── macd.ts │ │ ├── pointAndFigure.ts │ │ ├── renko.ts │ │ ├── rsi.ts │ │ ├── sar.ts │ │ ├── sma.ts │ │ ├── smoothedForceIndex.ts │ │ ├── sto.ts │ │ ├── tma.ts │ │ └── wma.ts │ ├── index.ts │ ├── indicator │ │ ├── algorithm.ts │ │ ├── atr.ts │ │ ├── baseIndicator.ts │ │ ├── bollingerBand.ts │ │ ├── change.ts │ │ ├── compare.ts │ │ ├── defaultOptionsForAppearance.ts │ │ ├── elderImpulse.ts │ │ ├── elderRay.ts │ │ ├── ema.ts │ │ ├── forceIndex.ts │ │ ├── heikinAshi.ts │ │ ├── index.ts │ │ ├── kagi.ts │ │ ├── macd.ts │ │ ├── pointAndFigure.ts │ │ ├── renko.ts │ │ ├── rsi.ts │ │ ├── sar.ts │ │ ├── sma.ts │ │ ├── stochasticOscillator.ts │ │ ├── tma.ts │ │ └── wma.ts │ └── utils │ │ ├── functor.ts │ │ ├── identity.ts │ │ ├── index.ts │ │ ├── mappedSlidingWindow.ts │ │ ├── merge.ts │ │ ├── path.ts │ │ ├── rebind.ts │ │ ├── slidingWindow.ts │ │ └── zipper.ts └── tsconfig.json ├── interactive ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── Brush.tsx │ ├── ClickCallback.tsx │ ├── DrawingObjectSelector.tsx │ ├── EquidistantChannel.tsx │ ├── FibonacciRetracement.tsx │ ├── GannFan.tsx │ ├── InteractiveText.tsx │ ├── InteractiveYCoordinate.tsx │ ├── StandardDeviationChannel.tsx │ ├── TrendLine.tsx │ ├── ZoomButtons.tsx │ ├── components │ │ ├── ChannelWithArea.tsx │ │ ├── ClickableCircle.tsx │ │ ├── ClickableShape.tsx │ │ ├── GannFan.tsx │ │ ├── HoverTextNearMouse.tsx │ │ ├── InteractiveStraightLine.tsx │ │ ├── InteractiveText.tsx │ │ ├── InteractiveYCoordinate.tsx │ │ ├── LinearRegressionChannelWithArea.tsx │ │ ├── MouseLocationIndicator.tsx │ │ ├── Text.tsx │ │ └── index.ts │ ├── index.ts │ ├── utils.ts │ └── wrapper │ │ ├── EachEquidistantChannel.tsx │ │ ├── EachFibRetracement.tsx │ │ ├── EachGannFan.tsx │ │ ├── EachInteractiveYCoordinate.tsx │ │ ├── EachLinearRegressionChannel.tsx │ │ ├── EachText.tsx │ │ ├── EachTrendLine.tsx │ │ └── index.ts └── tsconfig.json ├── scales ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── discontinuousTimeScaleProvider.ts │ ├── financeDiscontinuousScale.ts │ ├── index.ts │ ├── levels.ts │ └── timeFormat.ts └── tsconfig.json ├── series ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── AlternateDataSeries.tsx │ ├── AlternatingFillAreaSeries.tsx │ ├── AreaOnlySeries.tsx │ ├── AreaSeries.tsx │ ├── BarSeries.tsx │ ├── BollingerSeries.tsx │ ├── CandlestickSeries.tsx │ ├── ElderRaySeries.tsx │ ├── GroupedBarSeries.tsx │ ├── KagiSeries.tsx │ ├── LineSeries.tsx │ ├── MACDSeries.tsx │ ├── OHLCSeries.tsx │ ├── OverlayBarSeries.tsx │ ├── PointAndFigureSeries.tsx │ ├── RSISeries.tsx │ ├── RenkoSeries.tsx │ ├── SARSeries.tsx │ ├── SVGComponent.tsx │ ├── ScatterSeries.tsx │ ├── StackedBarSeries.tsx │ ├── StochasticSeries.tsx │ ├── StraightLine.tsx │ ├── VolumeProfileSeries.tsx │ ├── index.ts │ └── markers │ │ ├── CircleMarker.tsx │ │ ├── SquareMarker.tsx │ │ ├── TriangleMarker.tsx │ │ └── index.ts └── tsconfig.json ├── stories ├── .storybook │ ├── main.js │ ├── manager.js │ ├── preview-head.html │ └── preview.js ├── CHANGELOG.md ├── LICENSE ├── package.json ├── src │ ├── Intro.stories.mdx │ ├── data │ │ ├── DAILY.tsv │ │ ├── MINUTES.tsv │ │ ├── SECONDS.tsv │ │ ├── comparison.tsv │ │ ├── iOHLCData.ts │ │ ├── index.ts │ │ ├── withOHLCData.tsx │ │ └── withUpdatingData.tsx │ ├── features │ │ ├── FullCanvas.stories.tsx │ │ ├── StockChart.tsx │ │ ├── annotated │ │ │ ├── Annotated.tsx │ │ │ └── index.stories.tsx │ │ ├── annotations │ │ │ ├── Annotations.tsx │ │ │ └── index.stories.tsx │ │ ├── axis │ │ │ ├── Axis.tsx │ │ │ └── index.stories.tsx │ │ ├── coordinates │ │ │ ├── Coordinates.tsx │ │ │ └── index.stories.tsx │ │ ├── cursors │ │ │ ├── Cursors.tsx │ │ │ └── index.stories.tsx │ │ ├── edgeCases │ │ │ ├── BasicLineSeries.tsx │ │ │ └── index.stories.tsx │ │ ├── interaction │ │ │ ├── Interaction.tsx │ │ │ └── index.stories.tsx │ │ ├── scales │ │ │ ├── Scales.tsx │ │ │ └── index.stories.tsx │ │ ├── tooltips │ │ │ ├── Tooltips.tsx │ │ │ └── index.stories.tsx │ │ └── updating │ │ │ ├── BasicLineSeries.tsx │ │ │ └── index.stories.tsx │ ├── indicators │ │ ├── atr │ │ │ ├── AtrIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── bollingerBand │ │ │ ├── BollingerIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── compare │ │ │ ├── CompareIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── elderRay │ │ │ ├── ElderRayIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── ema │ │ │ ├── EmaIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── forceIndex │ │ │ ├── ForceIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── macd │ │ │ ├── MacdIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── rsi │ │ │ ├── RsiIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── sar │ │ │ ├── SarIndicator.tsx │ │ │ └── index.stories.tsx │ │ ├── sto │ │ │ ├── StoIndicator.tsx │ │ │ └── index.stories.tsx │ │ └── volumeProfile │ │ │ ├── VolumeProfile.tsx │ │ │ └── index.stories.tsx │ └── series │ │ ├── area │ │ ├── BasicAreaSeries.tsx │ │ └── index.stories.tsx │ │ ├── bar │ │ ├── BasicBarSeries.tsx │ │ └── index.stories.tsx │ │ ├── baseline │ │ ├── BasicBaselineSeries.tsx │ │ └── index.stories.tsx │ │ ├── candlestick │ │ ├── BasicCandlestick.tsx │ │ └── index.stories.tsx │ │ ├── heikinAshi │ │ ├── BasicHeikinAshiSeries.tsx │ │ └── index.stories.tsx │ │ ├── kagi │ │ ├── BasicKagiSeries.tsx │ │ └── index.stories.tsx │ │ ├── line │ │ ├── BasicLineSeries.tsx │ │ └── index.stories.tsx │ │ ├── ohlc │ │ ├── BasicOHLCSeries.tsx │ │ └── index.stories.tsx │ │ ├── pointAndFigure │ │ ├── BasicPointAndFigureSeries.tsx │ │ └── index.stories.tsx │ │ ├── renko │ │ ├── BasicRenkoSeries.tsx │ │ └── index.stories.tsx │ │ └── scatter │ │ ├── BasicScatterSeries.tsx │ │ └── index.stories.tsx └── tsconfig.json ├── tooltip ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── BollingerBandTooltip.tsx │ ├── GroupTooltip.tsx │ ├── HoverTooltip.tsx │ ├── MACDTooltip.tsx │ ├── MovingAverageTooltip.tsx │ ├── OHLCTooltip.tsx │ ├── RSITooltip.tsx │ ├── SingleTooltip.tsx │ ├── SingleValueTooltip.tsx │ ├── StochasticTooltip.tsx │ ├── ToolTipTSpanLabel.tsx │ ├── ToolTipText.tsx │ └── index.ts └── tsconfig.json └── utils ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src ├── index.ts ├── withDeviceRatio.tsx └── withSize.tsx └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{js,ts,py}] 14 | charset = utf-8 15 | 16 | # 4 space indentation 17 | [*.py] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | 25 | # Indentation override for all JS, TS 26 | [*.{js,ts,tsx}] 27 | indent_style = space 28 | indent_size = 4 29 | insert_final_newline = true 30 | 31 | # Matches the exact files either package.json or .travis.yml 32 | [{package.json,.travis.yml}] 33 | [{.json,.yml,.yaml}] 34 | indent_style = space 35 | indent_size = 2 36 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | lib 5 | # don't lint coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | root: true 3 | parser: "@typescript-eslint/parser" 4 | parserOptions: 5 | ecmaFeatures: 6 | jsx: true 7 | plugins: 8 | - "@typescript-eslint" 9 | extends: 10 | - "eslint:recommended" 11 | - "plugin:react/recommended" 12 | - "plugin:@typescript-eslint/eslint-recommended" 13 | - "plugin:@typescript-eslint/recommended" 14 | - "plugin:prettier/recommended" 15 | rules: 16 | "@typescript-eslint/explicit-member-accessibility": error 17 | "@typescript-eslint/explicit-module-boundary-types": off 18 | "@typescript-eslint/interface-name-prefix": off 19 | "@typescript-eslint/no-non-null-assertion": off 20 | "@typescript-eslint/ban-ts-comment": off 21 | "@typescript-eslint/ban-types": off 22 | "@typescript-eslint/no-explicit-any": off 23 | "@typescript-eslint/no-unused-vars": 24 | - error 25 | - ignoreRestSiblings: true 26 | "react/display-name": off 27 | "react/prop-types": off 28 | "arrow-spacing": error 29 | "curly": error 30 | "no-duplicate-imports": error 31 | "no-multiple-empty-lines": error 32 | "no-var": error 33 | "prefer-rest-params": 0 34 | settings: 35 | react: 36 | version: detect 37 | env: 38 | node: true 39 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js text eol=lf 2 | *.ts text eol=lf 3 | *.tsx text eol=lf 4 | -------------------------------------------------------------------------------- /.github/.dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: npm 5 | directory: "/" 6 | schedule: 7 | interval: weekly 8 | open-pull-requests-limit: 5 9 | reviewers: 10 | - markmcdowell 11 | versioning-strategy: increase 12 | commit-message: 13 | prefix: chore 14 | include: scope 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | commit-message: 20 | prefix: build 21 | include: scope 22 | -------------------------------------------------------------------------------- /.github/codeql-config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Default CodeQL config 3 | paths-ignore: 4 | - node_modules 5 | - '**/*.test.ts' 6 | - '**/dist/**/*' 7 | - '**/lib/**/*' 8 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | #### I'm submitting a... 2 | 3 | - [ ] bug 4 | - [ ] feature 5 | - [ ] chore 6 | 7 | #### What is the current behavior 8 | 9 | #### What is the expected behavior 10 | 11 | #### Please tell us about your environment 12 | 13 | - Version: 1.1.0 14 | - Browser: [all | Chrome XX | Firefox XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari ] 15 | 16 | #### Other information 17 | 18 | 21 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Number of days of inactivity before a closed issue or pull request is locked 3 | daysUntilLock: 7 4 | 5 | # Skip issues and pull requests created before a given timestamp. Timestamp must 6 | # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable 7 | skipCreatedBefore: false 8 | 9 | # Issues and pull requests with these labels will be ignored. Set to `[]` to disable 10 | exemptLabels: [] 11 | 12 | # Label to add before locking, such as `outdated`. Set to `false` to disable 13 | lockLabel: false 14 | 15 | # Comment to post before locking. Set to `false` to disable 16 | lockComment: > 17 | This thread has been automatically locked since there has not been 18 | any recent activity after it was closed. Please open a new issue for 19 | related bugs. 20 | 21 | # Assign `resolved` as the reason for locking. Set to `false` to disable 22 | setLockReason: true 23 | # Limit to only `issues` or `pulls` 24 | # only: issues 25 | 26 | # Optionally, specify configuration settings just for `issues` or `pulls` 27 | # issues: 28 | # exemptLabels: 29 | # - help-wanted 30 | # lockLabel: outdated 31 | 32 | # pulls: 33 | # daysUntilLock: 30 34 | 35 | # Repository to extend settings from 36 | # _extends: repo 37 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | #### Checklist 7 | - [ ] My commits follow [Conventional Commits](https://www.conventionalcommits.org) 8 | - [ ] documentation is updated 9 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Number of days of inactivity before an issue becomes stale 3 | daysUntilStale: 7 4 | 5 | # Number of days of inactivity before a stale issue is closed 6 | daysUntilClose: 7 7 | 8 | # Issues with these labels will never be considered stale 9 | exemptLabels: 10 | - pinned 11 | - security 12 | 13 | # Label to use when marking an issue as stale 14 | staleLabel: stale 15 | 16 | # Comment to post when marking an issue as stale. Set to `false` to disable 17 | markComment: > 18 | This issue has been automatically marked as stale because it has not had 19 | recent activity. It will be closed if no further activity occurs. Thank you 20 | for your contributions. 21 | 22 | # Comment to post when closing a stale issue. Set to `false` to disable 23 | closeComment: false 24 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: ci 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | jobs: 11 | ci: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 30 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | - name: Cache .npm directory 18 | uses: actions/cache@v2.1.1 19 | with: 20 | path: ~/.npm 21 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 22 | restore-keys: | 23 | ${{ runner.os }}-node- 24 | - name: Install npm dependencies 25 | run: | 26 | npm ci 27 | - name: Lint 28 | run: | 29 | npm run lint 30 | - name: Build 31 | run: | 32 | npm run build 33 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: GitHub CodeQL 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | schedule: 11 | - cron: '0 7 * * 6' 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | language: ['typescript'] 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v2 23 | - name: Initialize CodeQL 24 | uses: github/codeql-action/init@v1 25 | with: 26 | config-file: ./.github/codeql-config.yml 27 | languages: ${{ matrix.language }} 28 | - name: Install npm dependencies 29 | run: npm ci 30 | - name: Build 31 | run: npm run build 32 | - name: Perform CodeQL Analysis 33 | uses: github/codeql-action/analyze@v1 34 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: gh-pages 3 | on: 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 30 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Setup Node 15 | uses: actions/setup-node@v3.6.0 16 | with: 17 | node-version: "16" 18 | - name: Cache .npm directory 19 | uses: actions/cache@v2.1.1 20 | with: 21 | path: ~/.npm 22 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 23 | restore-keys: | 24 | ${{ runner.os }}-node- 25 | - name: Install npm dependencies 26 | run: | 27 | npm ci 28 | - name: Build 29 | run: | 30 | npm run docs 31 | - name: Deploy 32 | uses: crazy-max/ghaction-github-pages@v1 33 | with: 34 | target_branch: gh-pages 35 | build_dir: docs 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/npm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: NPM 3 | on: 4 | release: 5 | types: 6 | - published 7 | jobs: 8 | ci: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 30 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Cache .npm directory 15 | uses: actions/cache@v2.1.1 16 | with: 17 | path: ~/.npm 18 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 19 | restore-keys: | 20 | ${{ runner.os }}-node- 21 | - name: Install npm dependencies 22 | run: | 23 | npm ci 24 | - name: Publish packages to npm 25 | run: | 26 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc 27 | npm run publish 28 | env: 29 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | *.pid.lock 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # Bower dependency directory (https://bower.io/) 26 | bower_components 27 | 28 | # node-waf configuration 29 | .lock-wscript 30 | 31 | # Compiled binary addons (http://nodejs.org/api/addons.html) 32 | build/Release 33 | 34 | # Dependency directories 35 | node_modules/ 36 | jspm_packages/ 37 | 38 | # Typescript v1 declaration files 39 | typings/ 40 | 41 | # Optional npm cache directory 42 | .npm 43 | 44 | # Optional eslint cache 45 | .eslintcache 46 | 47 | # Optional REPL history 48 | .node_repl_history 49 | 50 | # Output of 'npm pack' 51 | *.tgz 52 | 53 | # Yarn Integrity file 54 | .yarn-integrity 55 | 56 | # dotenv environment variables file 57 | .env 58 | 59 | # build output 60 | docs/ 61 | dist/ 62 | lib/ 63 | server.js 64 | dump.rdb 65 | 66 | # ignore mac files 67 | .DS_Store 68 | 69 | .idea/ 70 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | bracketSpacing: true 3 | printWidth: 120 4 | semi: true 5 | singleQuote: false 6 | tabWidth: 4 7 | trailingComma: "all" 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "sourceMaps": true, 12 | "url": "http://localhost:4444", 13 | "breakOnLoad": true, 14 | "webRoot": "${workspaceFolder}/packages/stories", 15 | "sourceMapPathOverrides": { 16 | "webpack:///*": "${webRoot}/*", 17 | "webpack:///./*": "${webRoot}/*", 18 | "webpack:///src/*": "${webRoot}/*", 19 | "webpack:///./~/*": "${webRoot}/node_modules/*" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": true 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We welcome all contributions! 4 | 5 | Before creating a PR, please raise an issue to discuss it. 6 | 7 | ## Commits 8 | 9 | We use [convention commits](https://www.conventionalcommits.org) style of commit messages. 10 | 11 | ## Style 12 | 13 | The codebase is written in typescript, this is set to be strict with all warnings and errors turned on. We also use ts-lint. Both are run as part of the build. 14 | 15 | You can run `npm run lint` to show any style issues separate from the build. 16 | 17 | ### Guidelines 18 | 19 | * Use Promises instead callbacks 20 | * Files should be 100 lines or less 21 | 22 | ## Tests 23 | 24 | Tests are using storybook, please see existing tests for recommended formatting. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Versions greater than 1 are supported, please raise an issue if you find a vulnerability. 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | 1.x | :white_check_mark: | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | Please raise an issue and add the label `security`, this will disable any bots from marking the issue as stale or closing it. 14 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*"], 3 | "version": "2.0.1", 4 | "npmClient": "npm", 5 | "useWorkspaces": true, 6 | "command": { 7 | "bootstrap": { 8 | "hoist": true 9 | }, 10 | "publish": { 11 | "preDistTag": "next", 12 | "noGitReset": true 13 | }, 14 | "version": { 15 | "allowBranch": "main", 16 | "conventionalCommits": true, 17 | "createRelease": "github", 18 | "gitRemote": "upstream", 19 | "message": "chore(release): publish %v" 20 | } 21 | }, 22 | "ignoreChanges": ["**/__fixtures__/**", "**/__tests__/**", "**/*.md"] 23 | } 24 | -------------------------------------------------------------------------------- /packages/annotations/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/annotations/README.md: -------------------------------------------------------------------------------- 1 | # Annotations 2 | 3 | ```bash 4 | npm i @react-financial-charts/annotations 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/annotations/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/annotations", 3 | "version": "2.0.0", 4 | "description": "Annotations for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "peerDependencies": { 41 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 42 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 43 | }, 44 | "dependencies": { 45 | "@react-financial-charts/core": "file:../core", 46 | "@types/d3-scale": "^3.2.2" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/annotations/src/Annotate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { GenericChartComponent } from "@react-financial-charts/core"; 3 | 4 | export interface AnnotateProps { 5 | readonly className?: string; 6 | readonly with: React.ElementType; 7 | readonly when: (value: any, index: number, array: any[]) => boolean; 8 | readonly usingProps?: any; 9 | } 10 | 11 | export class Annotate extends React.Component { 12 | public static defaultProps = { 13 | className: 14 | "react-financial-charts-enable-interaction react-financial-charts-annotate react-financial-charts-default-cursor", 15 | }; 16 | 17 | public render() { 18 | return ; 19 | } 20 | 21 | private readonly renderSVG = (moreProps: any) => { 22 | const { 23 | xAccessor, 24 | xScale, 25 | chartConfig: { yScale }, 26 | plotData, 27 | } = moreProps; 28 | 29 | const { className, usingProps, with: Annotation, when } = this.props; 30 | 31 | const data = (plotData as unknown[]).filter(when); 32 | 33 | return ( 34 | 35 | {data.map((d, idx) => { 36 | return ( 37 | 46 | ); 47 | })} 48 | 49 | ); 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /packages/annotations/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Annotate"; 2 | export * from "./BarAnnotation"; 3 | export * from "./LabelAnnotation"; 4 | export * from "./SvgPathAnnotation"; 5 | export * from "./Label"; 6 | -------------------------------------------------------------------------------- /packages/annotations/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/axes/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/axes/README.md: -------------------------------------------------------------------------------- 1 | # Axes 2 | 3 | ```bash 4 | npm i @react-financial-charts/axes 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/axes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/axes", 3 | "version": "2.0.0", 4 | "description": "Axes for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "@react-financial-charts/core": "file:../core", 42 | "@types/d3-scale": "^3.2.2", 43 | "d3-array": "^2.9.1", 44 | "d3-force": "^2.1.1", 45 | "d3-scale": "^3.2.3", 46 | "d3-selection": "^2.0.0" 47 | }, 48 | "peerDependencies": { 49 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 50 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/axes/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Axis"; 2 | export * from "./XAxis"; 3 | export * from "./YAxis"; 4 | -------------------------------------------------------------------------------- /packages/axes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/charts/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/charts/README.md: -------------------------------------------------------------------------------- 1 | # React Financial Charts 2 | 3 | Charts dedicated to finance. 4 | 5 | The aim with this project is create financial charts that work out of the box. 6 | 7 | ## Features 8 | 9 | - integrates multiple chart types 10 | - over 60 technical indicators and overlays 11 | - drawing objects 12 | 13 | ### Chart types 14 | 15 | - Scatter 16 | - Area 17 | - Line 18 | - Candlestick 19 | - OHLC 20 | - HeikenAshi 21 | - Renko 22 | - Kagi 23 | - Point & Figure 24 | 25 | ### Indicators 26 | 27 | - EMA, SMA, WMA, TMA 28 | - Bollinger band 29 | - SAR 30 | - MACD 31 | - RSI 32 | - ATR 33 | - Stochastic (fast, slow, full) 34 | - ForceIndex 35 | - ElderRay 36 | - Elder Impulse 37 | 38 | ### Interactive Indicators 39 | 40 | - Trendline 41 | - Fibonacci Retracements 42 | - Gann Fan 43 | - Channel 44 | - Linear regression channel 45 | 46 | --- 47 | 48 | ## Installation 49 | 50 | ```sh 51 | npm install react-financial-charts 52 | ``` 53 | 54 | ## LICENSE 55 | 56 | [MIT](./LICENSE) 57 | -------------------------------------------------------------------------------- /packages/charts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-financial-charts", 3 | "version": "2.0.1", 4 | "description": "React charts specific to finance.", 5 | "main": "./lib/index.js", 6 | "typings": "./lib/index.d.ts", 7 | "files": [ 8 | "lib", 9 | "src" 10 | ], 11 | "sideEffects": false, 12 | "author": "reactivemarkets", 13 | "keywords": [ 14 | "charts", 15 | "charting", 16 | "stockcharts", 17 | "finance", 18 | "financial", 19 | "finance-chart", 20 | "react", 21 | "d3" 22 | ], 23 | "license": "MIT", 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 30 | }, 31 | "scripts": { 32 | "build": "npm run clean && npm run compile", 33 | "clean": "rimraf lib", 34 | "compile": "tsc -p tsconfig.json" 35 | }, 36 | "dependencies": { 37 | "@react-financial-charts/annotations": "file:../annotations", 38 | "@react-financial-charts/axes": "file:../axes", 39 | "@react-financial-charts/coordinates": "file:../coordinates", 40 | "@react-financial-charts/core": "file:../core", 41 | "@react-financial-charts/indicators": "file:../indicators", 42 | "@react-financial-charts/interactive": "file:../interactive", 43 | "@react-financial-charts/scales": "file:../scales", 44 | "@react-financial-charts/series": "file:../series", 45 | "@react-financial-charts/tooltip": "file:../tooltip", 46 | "@react-financial-charts/utils": "file:../utils" 47 | }, 48 | "peerDependencies": { 49 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 50 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/charts/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@react-financial-charts/annotations"; 2 | export * from "@react-financial-charts/axes"; 3 | export * from "@react-financial-charts/coordinates"; 4 | export * from "@react-financial-charts/core"; 5 | export * from "@react-financial-charts/indicators"; 6 | export * from "@react-financial-charts/interactive"; 7 | export * from "@react-financial-charts/scales"; 8 | export * from "@react-financial-charts/series"; 9 | export * from "@react-financial-charts/tooltip"; 10 | export * from "@react-financial-charts/utils"; 11 | -------------------------------------------------------------------------------- /packages/charts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/coordinates/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/coordinates/README.md: -------------------------------------------------------------------------------- 1 | # Coordinates 2 | 3 | ```bash 4 | npm i @react-financial-charts/coordinates 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/coordinates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/coordinates", 3 | "version": "2.0.0", 4 | "description": "Coordinates for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "@react-financial-charts/core": "file:../core", 42 | "d3-format": "^2.0.0" 43 | }, 44 | "peerDependencies": { 45 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 46 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/coordinates/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./EdgeIndicator"; 2 | export * from "./CrossHairCursor"; 3 | export * from "./CurrentCoordinate"; 4 | export * from "./Cursor"; 5 | export * from "./MouseCoordinateX"; 6 | export { MouseCoordinateXV2 } from "./MouseCoordinateXV2"; 7 | export { MouseCoordinateY } from "./MouseCoordinateY"; 8 | export * from "./PriceCoordinate"; 9 | -------------------------------------------------------------------------------- /packages/coordinates/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # Axes 2 | 3 | ```bash 4 | npm i @react-financial-charts/axes 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/core", 3 | "version": "2.0.0", 4 | "description": "Core code for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib coverage", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "@types/d3-scale": "^3.2.2", 42 | "d3-array": "^2.9.1", 43 | "d3-scale": "^3.2.3", 44 | "d3-selection": "^2.0.0", 45 | "lodash.flattendeep": "^4.4.0" 46 | }, 47 | "peerDependencies": { 48 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 49 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/core/src/CanvasContainer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface ICanvasContexts { 4 | readonly bg?: CanvasRenderingContext2D; 5 | readonly axes?: CanvasRenderingContext2D; 6 | readonly mouseCoord?: CanvasRenderingContext2D; 7 | } 8 | 9 | export interface CanvasContainerProps { 10 | readonly height: number; 11 | readonly ratio: number; 12 | readonly style?: React.CSSProperties; 13 | readonly width: number; 14 | } 15 | 16 | export class CanvasContainer extends React.PureComponent { 17 | private readonly bgRef = React.createRef(); 18 | private readonly axesRef = React.createRef(); 19 | private readonly mouseRef = React.createRef(); 20 | 21 | public getCanvasContexts(): ICanvasContexts { 22 | return { 23 | bg: this.bgRef.current?.getContext("2d") ?? undefined, 24 | axes: this.axesRef.current?.getContext("2d") ?? undefined, 25 | mouseCoord: this.mouseRef.current?.getContext("2d") ?? undefined, 26 | }; 27 | } 28 | 29 | public render() { 30 | const { height, ratio, style, width } = this.props; 31 | 32 | const adjustedWidth = width * ratio; 33 | const adjustedHeight = height * ratio; 34 | const canvasStyle: React.CSSProperties = { position: "absolute", width, height }; 35 | 36 | return ( 37 |
38 | 39 | 40 | 41 |
42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/core/src/MoreProps.ts: -------------------------------------------------------------------------------- 1 | import { ScaleContinuousNumeric } from "d3-scale"; 2 | import type { ChartConfig } from "./utils/ChartDataUtil"; 3 | 4 | export interface MoreProps { 5 | chartId: string | number; 6 | hovering: boolean; 7 | currentCharts: (string | number)[]; 8 | startPos?: [number, number]; 9 | mouseXY?: [number, number]; 10 | chartConfigs: ChartConfig[]; 11 | chartConfig?: ChartConfig; 12 | fullData: any[]; 13 | plotData: any[]; 14 | xAccessor: (datum: any) => any; 15 | xScale: Function; 16 | yScale?: ScaleContinuousNumeric; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { ChartCanvas, ChartCanvasContext } from "./ChartCanvas"; 2 | export * from "./Chart"; 3 | export * from "./GenericChartComponent"; 4 | export * from "./GenericComponent"; 5 | export * from "./MoreProps"; 6 | export * from "./utils"; 7 | export * from "./zoom"; 8 | -------------------------------------------------------------------------------- /packages/core/src/useEvent.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useLayoutEffect, useRef } from "react"; 2 | // Based on https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md#internal-implementation 3 | export function useEvent any>(handler: F): (...args: Parameters) => ReturnType { 4 | const handlerRef = useRef(); 5 | 6 | useLayoutEffect(() => { 7 | handlerRef.current = handler; 8 | }); 9 | 10 | return useCallback((...args: Parameters) => { 11 | const fn = handlerRef.current!; 12 | return fn(...args); 13 | }, []); 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/utils/PureComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallowEqual } from "./shallowEqual"; 3 | 4 | export class PureComponent extends React.Component { 5 | public shouldComponentUpdate(nextProps: T, nextState: S, nextContext: SS) { 6 | return ( 7 | !shallowEqual(this.props, nextProps) || 8 | !shallowEqual(this.state, nextState) || 9 | !shallowEqual(this.context, nextContext) 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/utils/barWidth.ts: -------------------------------------------------------------------------------- 1 | import { ScaleContinuousNumeric, ScaleTime } from "d3-scale"; 2 | import { first, last } from "."; 3 | 4 | /** 5 | * Bar width is based on the amount of items in the plot data and the distance between the first and last of those 6 | * items. 7 | * @param props the props passed to the series. 8 | * @param moreProps an object holding the xScale, xAccessor and plotData. 9 | * @return {number} the bar width. 10 | */ 11 | export const plotDataLengthBarWidth = ( 12 | props: { widthRatio: number }, 13 | moreProps: { 14 | xAccessor: (datum: T) => number | Date; 15 | xScale: ScaleContinuousNumeric | ScaleTime; 16 | plotData: T[]; 17 | }, 18 | ): number => { 19 | const { widthRatio } = props; 20 | const { xAccessor, xScale, plotData } = moreProps; 21 | 22 | const [l, r] = xScale.range(); 23 | 24 | if (xScale.invert != null) { 25 | const [dl, dr] = xScale.domain(); 26 | if (typeof dl === "number" && typeof dr === "number") { 27 | const totalWidth = Math.abs(r - l); 28 | 29 | const width = totalWidth / Math.abs(dl - dr); 30 | 31 | return width * widthRatio; 32 | } 33 | 34 | const width = xScale(xAccessor(last(plotData))) - xScale(xAccessor(first(plotData))); 35 | 36 | return (width / plotData.length) * widthRatio * 0.7; 37 | } 38 | 39 | const totalWidth = Math.abs(r - l); 40 | 41 | const width = totalWidth / xScale.domain().length; 42 | 43 | return width * widthRatio; 44 | }; 45 | -------------------------------------------------------------------------------- /packages/core/src/utils/closestItem.ts: -------------------------------------------------------------------------------- 1 | export const getClosestItemIndexes = ( 2 | array: T[], 3 | value: TAccessor, 4 | accessor: (item: T) => TAccessor, 5 | ) => { 6 | let lo = 0; 7 | let hi = array.length - 1; 8 | while (hi - lo > 1) { 9 | const mid = Math.round((lo + hi) / 2); 10 | const itemAtMid = array[mid]; 11 | const valueAtMid = accessor(itemAtMid); 12 | if (valueAtMid <= value) { 13 | lo = mid; 14 | } else { 15 | hi = mid; 16 | } 17 | } 18 | 19 | const lowItemValue = accessor(array[lo]); 20 | const highItemValue = accessor(array[hi]); 21 | 22 | // for Date object === does not work, so using the <= in combination with >= 23 | // the same code works for both dates and numbers 24 | if (lowItemValue?.valueOf() === value?.valueOf()) { 25 | hi = lo; 26 | } 27 | if (highItemValue?.valueOf() === value?.valueOf()) { 28 | lo = hi; 29 | } 30 | 31 | if (lowItemValue < value && highItemValue < value) { 32 | lo = hi; 33 | } 34 | if (lowItemValue > value && highItemValue > value) { 35 | hi = lo; 36 | } 37 | 38 | return { left: lo, right: hi }; 39 | }; 40 | 41 | export const getClosestItem = ( 42 | array: T[], 43 | value: TAccessor, 44 | accessor: (item: T) => TAccessor, 45 | ) => { 46 | const { left, right } = getClosestItemIndexes(array, value, accessor); 47 | if (left === right) { 48 | return array[left]; 49 | } 50 | 51 | const leftItem = accessor(array[left]); 52 | const rightItem = accessor(array[right]); 53 | 54 | const closest = 55 | Math.abs(leftItem.valueOf() - value.valueOf()) < Math.abs(rightItem.valueOf() - value.valueOf()) 56 | ? array[left] 57 | : array[right]; 58 | 59 | return closest; 60 | }; 61 | -------------------------------------------------------------------------------- /packages/core/src/utils/identity.ts: -------------------------------------------------------------------------------- 1 | export const identity = (d: any) => d; 2 | -------------------------------------------------------------------------------- /packages/core/src/utils/noop.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-empty-function 2 | export const noop = () => {}; 3 | -------------------------------------------------------------------------------- /packages/core/src/utils/shallowEqual.ts: -------------------------------------------------------------------------------- 1 | export const shallowEqual = (a: any, b: any) => { 2 | if (a === b) { 3 | return true; 4 | } 5 | if (!(a instanceof Object) || !(b instanceof Object)) { 6 | return false; 7 | } 8 | 9 | const keys = Object.keys(a); 10 | const length = keys.length; 11 | 12 | for (let i = 0; i < length; i++) { 13 | if (!(keys[i] in b)) { 14 | return false; 15 | } 16 | } 17 | 18 | for (let i = 0; i < length; i++) { 19 | if (a[keys[i]] !== b[keys[i]]) { 20 | return false; 21 | } 22 | } 23 | 24 | return length === Object.keys(b).length; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/core/src/utils/strokeDasharray.ts: -------------------------------------------------------------------------------- 1 | export type strokeDashTypes = 2 | | "Solid" 3 | | "ShortDash" 4 | | "ShortDash2" 5 | | "ShortDot" 6 | | "ShortDashDot" 7 | | "ShortDashDotDot" 8 | | "Dot" 9 | | "Dash" 10 | | "LongDash" 11 | | "DashDot" 12 | | "LongDashDot" 13 | | "LongDashDotDot"; 14 | 15 | export const getStrokeDasharrayCanvas = (type?: strokeDashTypes) => { 16 | const a = getStrokeDasharray(type).split(","); 17 | if (a.length === 1) { 18 | return []; 19 | } 20 | 21 | return a.map((d) => Number(d)); 22 | }; 23 | 24 | export const getStrokeDasharray = (type?: strokeDashTypes) => { 25 | switch (type) { 26 | default: 27 | case "Solid": 28 | return "none"; 29 | case "ShortDash": 30 | return "6, 2"; 31 | case "ShortDash2": 32 | return "6, 3"; 33 | case "ShortDot": 34 | return "2, 2"; 35 | case "ShortDashDot": 36 | return "6, 2, 2, 2"; 37 | case "ShortDashDotDot": 38 | return "6, 2, 2, 2, 2, 2"; 39 | case "Dot": 40 | return "2, 6"; 41 | case "Dash": 42 | return "4, 6"; 43 | case "LongDash": 44 | return "16, 6"; 45 | case "DashDot": 46 | return "8, 6, 2, 6"; 47 | case "LongDashDot": 48 | return "16, 6, 2, 6"; 49 | case "LongDashDotDot": 50 | return "16, 6, 2, 6, 2, 6"; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /packages/core/src/utils/zipper.ts: -------------------------------------------------------------------------------- 1 | /* an extension to d3.zip so we call a function instead of an array */ 2 | 3 | import { min } from "d3-array"; 4 | import { identity } from "./identity"; 5 | 6 | interface Zip { 7 | (...args: any[]): any[]; 8 | combine(): any; 9 | combine(x: any): Zip; 10 | } 11 | 12 | export default function zipper() { 13 | let combine = identity; 14 | 15 | function zip() { 16 | const n = arguments.length; 17 | if (n === 0) { 18 | return []; 19 | } 20 | const m = min(arguments, d3_zipLength) ?? 0; 21 | 22 | const zips = new Array(m); 23 | for (let i = -1; ++i < m; ) { 24 | // tslint:disable-next-line: no-shadowed-variable 25 | for (let j = -1, zip = (zips[i] = new Array(n)); ++j < n; ) { 26 | zip[j] = arguments[j][i]; 27 | } 28 | 29 | // @ts-ignore 30 | zips[i] = combine.apply(this, zips[i]); 31 | } 32 | return zips; 33 | } 34 | function d3_zipLength(d: any[]) { 35 | return d.length; 36 | } 37 | zip.combine = function (x: any) { 38 | if (!arguments.length) { 39 | return combine; 40 | } 41 | combine = x; 42 | return zip; 43 | }; 44 | return zip as Zip; 45 | } 46 | -------------------------------------------------------------------------------- /packages/core/src/zoom/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./zoomBehavior"; 2 | -------------------------------------------------------------------------------- /packages/core/src/zoom/zoomBehavior.ts: -------------------------------------------------------------------------------- 1 | import { ScaleContinuousNumeric, ScaleTime } from "d3-scale"; 2 | import { getCurrentItem } from "../utils/ChartDataUtil"; 3 | import { last } from "../utils/index"; 4 | 5 | export interface IZoomAnchorOptions { 6 | readonly plotData: TData[]; 7 | readonly mouseXY: number[]; 8 | readonly xAccessor: (data: TData) => TXAxis; 9 | readonly xScale: ScaleContinuousNumeric | ScaleTime; 10 | } 11 | 12 | export const mouseBasedZoomAnchor = ( 13 | options: IZoomAnchorOptions, 14 | ) => { 15 | const { xScale, xAccessor, mouseXY, plotData } = options; 16 | const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); 17 | return xAccessor(currentItem); 18 | }; 19 | 20 | export const lastVisibleItemBasedZoomAnchor = ( 21 | options: IZoomAnchorOptions, 22 | ) => { 23 | const { xAccessor, plotData } = options; 24 | const lastItem = last(plotData); 25 | return xAccessor(lastItem); 26 | }; 27 | 28 | export const rightDomainBasedZoomAnchor = ( 29 | options: IZoomAnchorOptions, 30 | ) => { 31 | const { xScale } = options; 32 | const [, end] = xScale.domain(); 33 | return end; 34 | }; 35 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/indicators/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/indicators/README.md: -------------------------------------------------------------------------------- 1 | # Indicators 2 | 3 | ```bash 4 | npm i @react-financial-charts/indicators 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/indicators/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/indicators", 3 | "version": "2.0.0", 4 | "description": "Indicators for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "d3-array": "^2.9.1", 42 | "d3-scale": "^3.2.3" 43 | }, 44 | "peerDependencies": { 45 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 46 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/change.ts: -------------------------------------------------------------------------------- 1 | import { slidingWindow } from "../utils"; 2 | import { Change as defaultOptions } from "./defaultOptionsForComputation"; 3 | 4 | export interface ChangeOptions { 5 | readonly sourcePath: string; 6 | readonly basePath: string; 7 | readonly mainKeys: string[]; 8 | readonly compareKeys: string[]; 9 | } 10 | 11 | interface ChangeCalculator { 12 | (data: any[]): any; 13 | undefinedLength(): number; 14 | options(): ChangeOptions; 15 | options(newOptions: ChangeOptions): ChangeCalculator; 16 | } 17 | 18 | export default function () { 19 | let options: ChangeOptions = defaultOptions; 20 | 21 | const calculator = (data: any[]) => { 22 | const { sourcePath } = options; 23 | 24 | const algo = slidingWindow() 25 | .windowSize(2) 26 | .sourcePath(sourcePath) 27 | .accumulator(([prev, curr]: any) => { 28 | const absoluteChange = curr - prev; 29 | const percentChange = (absoluteChange * 100) / prev; 30 | return { absoluteChange, percentChange }; 31 | }); 32 | 33 | const newData = algo(data); 34 | 35 | return newData; 36 | }; 37 | 38 | calculator.undefinedLength = () => { 39 | return 1; 40 | }; 41 | 42 | calculator.options = (newOptions?: ChangeOptions) => { 43 | if (newOptions === undefined) { 44 | return options; 45 | } 46 | 47 | options = { ...defaultOptions, ...newOptions }; 48 | 49 | return calculator; 50 | }; 51 | 52 | return calculator as ChangeCalculator; 53 | } 54 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/compare.ts: -------------------------------------------------------------------------------- 1 | import { path } from "../utils"; 2 | import { Change as defaultOptions } from "./defaultOptionsForComputation"; 3 | 4 | export interface CompareOptions { 5 | readonly basePath: string; 6 | readonly compareKeys: string[]; 7 | readonly mainKeys: string[]; 8 | readonly sourcePath?: string; 9 | } 10 | 11 | export default function () { 12 | let options: CompareOptions = defaultOptions; 13 | 14 | const calculator = (data: any[]) => { 15 | const { basePath, mainKeys, compareKeys } = options; 16 | 17 | const base = path(basePath); 18 | 19 | const first = data[0]; 20 | const b = base(first); 21 | 22 | const firsts: any = {}; 23 | 24 | const compareData = data.map((d) => { 25 | const result = {}; 26 | 27 | mainKeys.forEach((key) => { 28 | if (typeof d[key] === "object") { 29 | // @ts-ignore 30 | result[key] = {}; 31 | Object.keys(d[key]).forEach((subkey) => { 32 | // @ts-ignore 33 | result[key][subkey] = (d[key][subkey] - b) / b; 34 | }); 35 | } else { 36 | // @ts-ignore 37 | result[key] = (d[key] - b) / b; 38 | } 39 | }); 40 | 41 | compareKeys.forEach((key) => { 42 | if (d[key] !== undefined && firsts[key] === undefined) { 43 | // @ts-ignore 44 | firsts[key] = d[key]; 45 | } 46 | if (d[key] !== undefined && firsts[key] !== undefined) { 47 | // @ts-ignore 48 | result[key] = (d[key] - firsts[key]) / firsts[key]; 49 | } 50 | }); 51 | return result; 52 | }); 53 | 54 | return compareData; 55 | }; 56 | 57 | calculator.options = (newOptions?: CompareOptions) => { 58 | if (newOptions === undefined) { 59 | return options; 60 | } 61 | 62 | options = { ...defaultOptions, ...newOptions }; 63 | 64 | return calculator; 65 | }; 66 | 67 | return calculator; 68 | } 69 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/forceIndex.ts: -------------------------------------------------------------------------------- 1 | import { path, slidingWindow } from "../utils"; 2 | import { ForceIndex as defaultOptions } from "./defaultOptionsForComputation"; 3 | 4 | export interface ForceIndexOptions { 5 | readonly sourcePath: string; 6 | readonly volumePath: string; 7 | } 8 | 9 | export default function () { 10 | let options = defaultOptions; 11 | 12 | const calculator = (data: any[]) => { 13 | const { sourcePath, volumePath } = options; 14 | 15 | const source = path(sourcePath); 16 | 17 | const volume = path(volumePath); 18 | 19 | const forceIndexCalulator = slidingWindow() 20 | .windowSize(2) 21 | .accumulator(([prev, curr]: any) => (source(curr) - source(prev)) * volume(curr)); 22 | 23 | const forceIndex = forceIndexCalulator(data); 24 | 25 | return forceIndex; 26 | }; 27 | 28 | calculator.undefinedLength = () => { 29 | return 2; 30 | }; 31 | 32 | calculator.options = (newOptions?: ForceIndexOptions) => { 33 | if (newOptions === undefined) { 34 | return options; 35 | } 36 | 37 | options = { ...defaultOptions, ...newOptions }; 38 | 39 | return calculator; 40 | }; 41 | 42 | return calculator; 43 | } 44 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/heikinAshi.ts: -------------------------------------------------------------------------------- 1 | import { mappedSlidingWindow } from "../utils"; 2 | 3 | export default function () { 4 | let source = (x: any) => x; 5 | 6 | const calculator = (data: any[]) => { 7 | const algorithm = mappedSlidingWindow() 8 | .windowSize(2) 9 | .undefinedValue( 10 | ({ open, high, low, close }: { open: number; high: number; low: number; close: number }) => { 11 | close = (open + high + low + close) / 4; 12 | return { open, high, low, close }; 13 | }, 14 | ) 15 | .accumulator(([prev, now]: any) => { 16 | const { date, volume } = now; 17 | const close = (now.open + now.high + now.low + now.close) / 4; 18 | const open = (prev.open + prev.close) / 2; 19 | const high = Math.max(open, now.high, close); 20 | const low = Math.min(open, now.low, close); 21 | return { date, open, high, low, close, volume }; 22 | }); 23 | 24 | return algorithm(data); 25 | }; 26 | 27 | calculator.source = (newSource?: any) => { 28 | if (newSource === undefined) { 29 | return source; 30 | } 31 | 32 | source = newSource; 33 | 34 | return calculator; 35 | }; 36 | 37 | return calculator; 38 | } 39 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ema } from "./ema"; 2 | export { default as sma } from "./sma"; 3 | export { default as wma } from "./wma"; 4 | export { default as tma } from "./tma"; 5 | export { default as bollingerband } from "./bollingerband"; 6 | export { default as heikinAshi } from "./heikinAshi"; 7 | export { default as kagi } from "./kagi"; 8 | export { default as pointAndFigure } from "./pointAndFigure"; 9 | export { default as renko } from "./renko"; 10 | export { default as macd } from "./macd"; 11 | export { default as rsi } from "./rsi"; 12 | export { default as sto } from "./sto"; 13 | export { default as atr } from "./atr"; 14 | export { default as forceIndex } from "./forceIndex"; 15 | export { default as smoothedForceIndex } from "./smoothedForceIndex"; 16 | export { default as elderRay } from "./elderRay"; 17 | export { default as sar } from "./sar"; 18 | export { default as compare } from "./compare"; 19 | export { default as change } from "./change"; 20 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/sma.ts: -------------------------------------------------------------------------------- 1 | import { mean } from "d3-array"; 2 | import { slidingWindow } from "../utils"; 3 | import { SMA as defaultOptions } from "./defaultOptionsForComputation"; 4 | 5 | export interface SMAOptions { 6 | readonly sourcePath?: string; 7 | readonly windowSize: number; 8 | } 9 | 10 | export default function () { 11 | let options = defaultOptions; 12 | 13 | const calculator = (data: any[]) => { 14 | const { windowSize, sourcePath } = options; 15 | 16 | const average = slidingWindow() 17 | .windowSize(windowSize) 18 | .sourcePath(sourcePath) 19 | .accumulator((values: any[]) => mean(values)); 20 | 21 | return average(data); 22 | }; 23 | 24 | calculator.undefinedLength = () => { 25 | const { windowSize } = options; 26 | 27 | return windowSize - 1; 28 | }; 29 | 30 | calculator.options = (newOptions?: SMAOptions) => { 31 | if (newOptions === undefined) { 32 | return options; 33 | } 34 | 35 | options = { ...defaultOptions, ...newOptions }; 36 | 37 | return calculator; 38 | }; 39 | 40 | return calculator; 41 | } 42 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/smoothedForceIndex.ts: -------------------------------------------------------------------------------- 1 | import { zip } from "d3-array"; 2 | import { SmoothedForceIndex as defaultOptions } from "./defaultOptionsForComputation"; 3 | import ema from "./ema"; 4 | import forceIndex from "./forceIndex"; 5 | import sma from "./sma"; 6 | 7 | export default function () { 8 | const underlyingAlgorithm = forceIndex(); 9 | 10 | let options = defaultOptions; 11 | 12 | const calculator = (data: any[]) => { 13 | const { smoothingType, smoothingWindow } = options; 14 | const { sourcePath, volumePath } = options; 15 | 16 | const algo = underlyingAlgorithm.options({ sourcePath, volumePath }); 17 | 18 | // @ts-ignore 19 | const force = algo(data); 20 | 21 | const ma = smoothingType === "ema" ? ema() : sma(); 22 | const forceMA = ma.options({ 23 | windowSize: smoothingWindow, 24 | sourcePath: undefined, 25 | }); 26 | 27 | // @ts-ignore 28 | const smoothed = forceMA(force); 29 | 30 | return zip(force, smoothed).map((d) => ({ 31 | force: d[0], 32 | smoothed: d[1], 33 | })); 34 | }; 35 | 36 | calculator.undefinedLength = () => { 37 | const { smoothingWindow } = options; 38 | 39 | return underlyingAlgorithm.undefinedLength() + smoothingWindow - 1; 40 | }; 41 | 42 | calculator.options = (newOptions?: any) => { 43 | if (newOptions === undefined) { 44 | return options; 45 | } 46 | 47 | options = { ...defaultOptions, ...newOptions }; 48 | 49 | return calculator; 50 | }; 51 | 52 | return calculator; 53 | } 54 | -------------------------------------------------------------------------------- /packages/indicators/src/calculator/wma.ts: -------------------------------------------------------------------------------- 1 | import { sum } from "d3-array"; 2 | import { slidingWindow } from "../utils"; 3 | import { WMA as defaultOptions } from "./defaultOptionsForComputation"; 4 | 5 | export interface WMAOptions { 6 | sourcePath?: string; 7 | windowSize: number; 8 | } 9 | 10 | export default function () { 11 | let options = defaultOptions; 12 | 13 | const calculator = (data: any[]) => { 14 | const { windowSize, sourcePath } = options; 15 | 16 | const weight = (windowSize * (windowSize + 1)) / 2; 17 | 18 | const waverage = slidingWindow() 19 | .windowSize(windowSize) 20 | .sourcePath(sourcePath) 21 | .accumulator((values: number[]) => { 22 | const total = sum(values, (v, i) => { 23 | return (i + 1) * v; 24 | }); 25 | 26 | return total / weight; 27 | }); 28 | 29 | return waverage(data); 30 | }; 31 | 32 | calculator.undefinedLength = () => { 33 | const { windowSize } = options; 34 | 35 | return windowSize - 1; 36 | }; 37 | 38 | calculator.options = (newOptions?: WMAOptions) => { 39 | if (newOptions === undefined) { 40 | return options; 41 | } 42 | 43 | options = { ...defaultOptions, ...newOptions }; 44 | 45 | return calculator; 46 | }; 47 | 48 | return calculator; 49 | } 50 | -------------------------------------------------------------------------------- /packages/indicators/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./indicator"; 2 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/algorithm.ts: -------------------------------------------------------------------------------- 1 | import { identity, merge, slidingWindow } from "../utils"; 2 | 3 | export interface Algorithm { 4 | (data: any[]): any; 5 | accumulator(): any; 6 | accumulator(newAccumulator: any): Algorithm; 7 | windowSize(): number; 8 | windowSize(windowSize: number): Algorithm; 9 | merge(): any; 10 | merge(newMerge: any): Algorithm; 11 | } 12 | 13 | export default function () { 14 | let windowSize = 1; 15 | let accumulator = identity; 16 | let mergeAs = identity; 17 | 18 | function algorithm(data: any[]) { 19 | const defaultAlgorithm = slidingWindow().windowSize(windowSize).accumulator(accumulator); 20 | 21 | const calculator = merge().algorithm(defaultAlgorithm).merge(mergeAs); 22 | 23 | const newData = calculator(data); 24 | 25 | return newData; 26 | } 27 | 28 | algorithm.accumulator = (newAccumulator?: any) => { 29 | if (newAccumulator === undefined) { 30 | return accumulator; 31 | } 32 | accumulator = newAccumulator; 33 | return algorithm; 34 | }; 35 | 36 | algorithm.windowSize = (newWindowSize?: number) => { 37 | if (newWindowSize === undefined) { 38 | return windowSize; 39 | } 40 | windowSize = newWindowSize; 41 | return algorithm; 42 | }; 43 | 44 | algorithm.merge = (newMerge?: any) => { 45 | if (newMerge === undefined) { 46 | return mergeAs; 47 | } 48 | mergeAs = newMerge; 49 | return algorithm; 50 | }; 51 | 52 | return algorithm as Algorithm; 53 | } 54 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/atr.ts: -------------------------------------------------------------------------------- 1 | import { atr } from "../calculator"; 2 | import { merge, rebind } from "../utils"; 3 | import { ATROptions } from "../calculator/atr"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "ATR"; 7 | 8 | interface ATRIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): ATRIndicator; 12 | accessor(): any; 13 | accessor(x: any): ATRIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): ATRIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): ATRIndicator; 18 | echo(): any; 19 | echo(x: any): ATRIndicator; 20 | type(): string; 21 | type(x: string): ATRIndicator; 22 | merge(): any; 23 | merge(newMerge: any): ATRIndicator; 24 | options(): ATROptions; 25 | options(newOptions: ATROptions): ATRIndicator; 26 | skipUndefined(): boolean; 27 | skipUndefined(newSkipUndefined: boolean): ATRIndicator; 28 | } 29 | 30 | export default function () { 31 | const base = baseIndicator().type(ALGORITHM_TYPE); 32 | 33 | const underlyingAlgorithm = atr(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.atr = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | 50 | return underlyingAlgorithm(data); 51 | }; 52 | 53 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 54 | rebind(indicator, underlyingAlgorithm, "options"); 55 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 56 | 57 | return indicator as ATRIndicator; 58 | } 59 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/baseIndicator.ts: -------------------------------------------------------------------------------- 1 | import { scaleOrdinal } from "d3-scale"; 2 | 3 | const defaultColors = ["#F44336", "#2196F3", "#8BC34A", "#FF5722", "#3F51B5", "#03A9F4", "#9C27B0", "#4CAF50"]; 4 | 5 | let i = 0; 6 | const overlayColors = scaleOrdinal(defaultColors); 7 | 8 | export interface BaseIndicator { 9 | (): () => void; 10 | id(): number; 11 | id(x: number): BaseIndicator; 12 | accessor(): any; 13 | accessor(x: any): BaseIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): BaseIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): BaseIndicator; 18 | echo(): any; 19 | echo(x: any): BaseIndicator; 20 | type(): string; 21 | type(x: string): BaseIndicator; 22 | } 23 | 24 | export default function () { 25 | let id = i++; 26 | let accessor: any; 27 | let stroke: string | any; 28 | let fill: string | any; 29 | let echo: any; 30 | let type: string; 31 | 32 | const baseIndicator = () => () => { 33 | /** Do Nothing */ 34 | }; 35 | 36 | baseIndicator.id = (newId?: number) => { 37 | if (newId === undefined) { 38 | return id; 39 | } 40 | 41 | id = newId; 42 | 43 | return baseIndicator; 44 | }; 45 | 46 | baseIndicator.accessor = (newAccessor?: any) => { 47 | if (newAccessor === undefined) { 48 | return accessor; 49 | } 50 | 51 | accessor = newAccessor; 52 | 53 | return baseIndicator; 54 | }; 55 | 56 | baseIndicator.stroke = (newStroke?: string | any) => { 57 | if (newStroke === undefined) { 58 | return !stroke ? (stroke = overlayColors(id)) : stroke; 59 | } 60 | 61 | stroke = newStroke; 62 | 63 | return baseIndicator; 64 | }; 65 | 66 | baseIndicator.fill = (newFill?: string | any) => { 67 | if (newFill === undefined) { 68 | return !fill ? (fill = overlayColors(id)) : fill; 69 | } 70 | 71 | fill = newFill; 72 | 73 | return baseIndicator; 74 | }; 75 | 76 | baseIndicator.echo = (newEcho?: any) => { 77 | if (newEcho === undefined) { 78 | return echo; 79 | } 80 | 81 | echo = newEcho; 82 | 83 | return baseIndicator; 84 | }; 85 | 86 | baseIndicator.type = (newType?: string) => { 87 | if (newType === undefined) { 88 | return type; 89 | } 90 | 91 | type = newType; 92 | 93 | return baseIndicator; 94 | }; 95 | 96 | return baseIndicator as BaseIndicator; 97 | } 98 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/bollingerBand.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { bollingerband } from "../calculator"; 3 | import { BollingerBandOptions } from "../calculator/bollingerband"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "BollingerBand"; 7 | 8 | interface BollingerBandIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): BollingerBandIndicator; 12 | accessor(): any; 13 | accessor(x: any): BollingerBandIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): BollingerBandIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): BollingerBandIndicator; 18 | echo(): any; 19 | echo(x: any): BollingerBandIndicator; 20 | type(): string; 21 | type(x: string): BollingerBandIndicator; 22 | merge(): any; 23 | merge(newMerge: any): BollingerBandIndicator; 24 | options(): BollingerBandOptions; 25 | options(newOptions: BollingerBandOptions): BollingerBandIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator().type(ALGORITHM_TYPE); 30 | 31 | const underlyingAlgorithm = bollingerband(); 32 | 33 | const mergedAlgorithm = merge() 34 | .algorithm(underlyingAlgorithm) 35 | .merge((datum: any, i: number) => { 36 | datum.bollingerBand = i; 37 | }); 38 | 39 | const indicator = (data: any[], options = { merge: true }) => { 40 | if (options.merge) { 41 | if (!base.accessor()) { 42 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 43 | } 44 | 45 | return mergedAlgorithm(data); 46 | } 47 | return underlyingAlgorithm(data); 48 | }; 49 | 50 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 51 | rebind(indicator, underlyingAlgorithm, "options"); 52 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 53 | 54 | return indicator as BollingerBandIndicator; 55 | } 56 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/change.ts: -------------------------------------------------------------------------------- 1 | import { change } from "../calculator"; 2 | import { merge, rebind } from "../utils"; 3 | import { ChangeOptions } from "../calculator/change"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "Change"; 7 | 8 | interface ChangeIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): ChangeIndicator; 12 | accessor(): any; 13 | accessor(x: any): ChangeIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): ChangeIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): ChangeIndicator; 18 | echo(): any; 19 | echo(x: any): ChangeIndicator; 20 | type(): string; 21 | type(x: string): ChangeIndicator; 22 | merge(): any; 23 | merge(newMerge: any): ChangeIndicator; 24 | options(): ChangeOptions; 25 | options(newOptions: ChangeOptions): ChangeIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator().type(ALGORITHM_TYPE); 30 | 31 | const underlyingAlgorithm = change(); 32 | 33 | const mergedAlgorithm = merge() 34 | .algorithm(underlyingAlgorithm) 35 | .merge((datum: any, i: any) => { 36 | datum.absoluteChange = i.absoluteChange; 37 | datum.percentChange = i.percentChange; 38 | }); 39 | 40 | const indicator = (data: any[], options = { merge: true }) => { 41 | if (options.merge) { 42 | return mergedAlgorithm(data); 43 | } 44 | return underlyingAlgorithm(data); 45 | }; 46 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 47 | rebind(indicator, underlyingAlgorithm, "options"); 48 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 49 | 50 | return indicator as ChangeIndicator; 51 | } 52 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/compare.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { compare } from "../calculator"; 3 | import { CompareOptions } from "../calculator/compare"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "Compare"; 7 | 8 | interface CompareIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): CompareIndicator; 12 | accessor(): any; 13 | accessor(x: any): CompareIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): CompareIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): CompareIndicator; 18 | echo(): any; 19 | echo(x: any): CompareIndicator; 20 | type(): string; 21 | type(x: string): CompareIndicator; 22 | merge(): any; 23 | merge(newMerge: any): CompareIndicator; 24 | options(): CompareOptions; 25 | options(newOptions: CompareOptions): CompareIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.compare); 32 | 33 | const underlyingAlgorithm = compare(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.compare = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | return underlyingAlgorithm(data); 50 | }; 51 | 52 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 53 | rebind(indicator, underlyingAlgorithm, "options"); 54 | rebind(indicator, mergedAlgorithm, "merge"); 55 | 56 | return indicator as CompareIndicator; 57 | } 58 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/defaultOptionsForAppearance.ts: -------------------------------------------------------------------------------- 1 | export const themes = { 2 | light: { 3 | BollingerBand: { 4 | stroke: { 5 | top: "#964B00", 6 | middle: "#000000", 7 | bottom: "#964B00", 8 | }, 9 | fill: "#4682B4", 10 | }, 11 | ElderImpulse: { 12 | stroke: { 13 | up: "#6BA583", 14 | down: "#FF0000", 15 | neutral: "#0000FF", 16 | }, 17 | }, 18 | MACD: { 19 | fill: { 20 | divergence: "#4682B4", 21 | }, 22 | stroke: { 23 | macd: "#FF0000", 24 | signal: "#00F300", 25 | }, 26 | }, 27 | FullStochasticOscillator: { 28 | stroke: { 29 | top: "#37a600", 30 | middle: "#b8ab00", 31 | bottom: "#37a600", 32 | dLine: "#17becf", 33 | kLine: "#ff7f0e", 34 | }, 35 | }, 36 | }, 37 | dark: { 38 | BollingerBand: { 39 | stroke: { 40 | top: "#964B00", 41 | middle: "#FF6600", 42 | bottom: "#964B00", 43 | }, 44 | fill: "#4682B4", 45 | }, 46 | ElderImpulse: { 47 | stroke: { 48 | up: "#6BA583", 49 | down: "#FF0000", 50 | neutral: "#0000FF", 51 | }, 52 | }, 53 | MACD: { 54 | fill: { 55 | divergence: "#FF6600", 56 | }, 57 | stroke: { 58 | macd: "#ea2bff", 59 | signal: "#74d400", 60 | }, 61 | }, 62 | FullStochasticOscillator: { 63 | stroke: { 64 | top: "#37a600", 65 | middle: "#b8ab00", 66 | bottom: "#37a600", 67 | dLine: "#ea2bff", 68 | kLine: "#74d400", 69 | }, 70 | }, 71 | }, 72 | }; 73 | 74 | export const BollingerBand = themes.light.BollingerBand; 75 | export const ElderImpulse = themes.light.ElderImpulse; 76 | export const MACD = themes.light.MACD; 77 | export const FullStochasticOscillator = themes.light.FullStochasticOscillator; 78 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/elderRay.ts: -------------------------------------------------------------------------------- 1 | import { elderRay } from "../calculator"; 2 | import { merge, rebind } from "../utils"; 3 | import { ElderRayOptions } from "../calculator/elderRay"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "ElderRay"; 7 | 8 | interface ElderRayIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): ElderRayIndicator; 12 | accessor(): any; 13 | accessor(x: any): ElderRayIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): ElderRayIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): ElderRayIndicator; 18 | echo(): any; 19 | echo(x: any): ElderRayIndicator; 20 | type(): string; 21 | type(x: string): ElderRayIndicator; 22 | merge(): any; 23 | merge(newMerge: any): ElderRayIndicator; 24 | options(): ElderRayOptions; 25 | options(newOptions: ElderRayOptions): ElderRayIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.elderRay); 32 | 33 | const underlyingAlgorithm = elderRay(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.elderRay = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | return mergedAlgorithm(data); 47 | } 48 | return underlyingAlgorithm(data); 49 | }; 50 | 51 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 52 | rebind(indicator, underlyingAlgorithm, "options"); 53 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 54 | 55 | return indicator as ElderRayIndicator; 56 | } 57 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/ema.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { ema } from "../calculator"; 3 | import { EMAOptions } from "../calculator/ema"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "EMA"; 7 | 8 | interface EMAIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): EMAIndicator; 12 | accessor(): any; 13 | accessor(x: any): EMAIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): EMAIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): EMAIndicator; 18 | echo(): any; 19 | echo(x: any): EMAIndicator; 20 | type(): string; 21 | type(x: string): EMAIndicator; 22 | merge(): any; 23 | merge(newMerge: any): EMAIndicator; 24 | options(): EMAOptions; 25 | options(newOptions: EMAOptions): EMAIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.ema); 32 | 33 | const underlyingAlgorithm = ema(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.ema = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | 50 | return underlyingAlgorithm(data); 51 | }; 52 | 53 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 54 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 55 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 56 | 57 | return indicator as EMAIndicator; 58 | } 59 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/forceIndex.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { forceIndex } from "../calculator"; 3 | import { ForceIndexOptions } from "../calculator/forceIndex"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "ForceIndex"; 7 | 8 | interface ForceIndexIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): ForceIndexIndicator; 12 | accessor(): any; 13 | accessor(x: any): ForceIndexIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): ForceIndexIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): ForceIndexIndicator; 18 | echo(): any; 19 | echo(x: any): ForceIndexIndicator; 20 | type(): string; 21 | type(x: string): ForceIndexIndicator; 22 | merge(): any; 23 | merge(newMerge: any): ForceIndexIndicator; 24 | options(): ForceIndexOptions; 25 | options(newOptions: ForceIndexOptions): ForceIndexIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.forceIndex); 32 | 33 | const underlyingAlgorithm = forceIndex(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.forceIndex = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | return underlyingAlgorithm(data); 50 | }; 51 | 52 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 53 | rebind(indicator, underlyingAlgorithm, "options"); 54 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 55 | 56 | return indicator as ForceIndexIndicator; 57 | } 58 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/heikinAshi.ts: -------------------------------------------------------------------------------- 1 | import { heikinAshi } from "../calculator"; 2 | import baseIndicator from "./baseIndicator"; 3 | import { merge, rebind } from "../utils"; 4 | 5 | const ALGORITHM_TYPE = "HeikinAshi"; 6 | 7 | export default function () { 8 | const base = baseIndicator() 9 | .type(ALGORITHM_TYPE) 10 | .accessor((d: any) => d.ha); 11 | 12 | const underlyingAlgorithm = heikinAshi(); 13 | 14 | const mergedAlgorithm = merge() 15 | .algorithm(underlyingAlgorithm) 16 | .merge((datum: any, i: any) => { 17 | return { ...datum, ...i }; 18 | }); 19 | 20 | const indicator = (data: any[], options = { merge: true }) => { 21 | if (options.merge) { 22 | if (!base.accessor()) { 23 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 24 | } 25 | 26 | return mergedAlgorithm(data); 27 | } 28 | return underlyingAlgorithm(data); 29 | }; 30 | 31 | rebind(indicator, base, "accessor", "stroke", "fill", "echo", "type"); 32 | rebind(indicator, mergedAlgorithm, "merge"); 33 | 34 | return indicator; 35 | } 36 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/index.ts: -------------------------------------------------------------------------------- 1 | export { default as algo } from "./algorithm"; 2 | export { default as ema } from "./ema"; 3 | export { default as sma } from "./sma"; 4 | export { default as wma } from "./wma"; 5 | export { default as tma } from "./tma"; 6 | export { default as bollingerBand } from "./bollingerBand"; 7 | export { default as heikinAshi } from "./heikinAshi"; 8 | export { default as kagi } from "./kagi"; 9 | export { default as pointAndFigure } from "./pointAndFigure"; 10 | export { default as renko } from "./renko"; 11 | export { default as macd } from "./macd"; 12 | export { default as rsi } from "./rsi"; 13 | export { default as atr } from "./atr"; 14 | export { default as stochasticOscillator } from "./stochasticOscillator"; 15 | export { default as forceIndex } from "./forceIndex"; 16 | export { default as sar } from "./sar"; 17 | export { default as elderRay } from "./elderRay"; 18 | export { default as change } from "./change"; 19 | export { default as elderImpulse } from "./elderImpulse"; 20 | export { default as compare } from "./compare"; 21 | 22 | import * as defaultOptionsForComputation from "../calculator/defaultOptionsForComputation"; 23 | import * as defaultOptionsForAppearance from "./defaultOptionsForAppearance"; 24 | 25 | export { defaultOptionsForComputation, defaultOptionsForAppearance }; 26 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/kagi.ts: -------------------------------------------------------------------------------- 1 | import { rebind } from "../utils"; 2 | import { kagi } from "../calculator"; 3 | import baseIndicator from "./baseIndicator"; 4 | 5 | const ALGORITHM_TYPE = "Kagi"; 6 | 7 | export default function () { 8 | const base = baseIndicator().type(ALGORITHM_TYPE); 9 | 10 | const underlyingAlgorithm = kagi(); 11 | 12 | const indicator = underlyingAlgorithm; 13 | 14 | rebind(indicator, base, "id", "stroke", "fill", "echo", "type"); 15 | rebind(indicator, underlyingAlgorithm, "dateAccessor", "dateMutator", "options"); 16 | 17 | return indicator; 18 | } 19 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/macd.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { macd } from "../calculator"; 3 | import { MACDOptions } from "../calculator/macd"; 4 | import baseIndicator from "./baseIndicator"; 5 | import { MACD as appearanceOptions } from "./defaultOptionsForAppearance"; 6 | 7 | const ALGORITHM_TYPE = "MACD"; 8 | 9 | interface MACDIndicator { 10 | (data: any[], options?: { merge: boolean }): any; 11 | id(): number; 12 | id(x: number): MACDIndicator; 13 | accessor(): any; 14 | accessor(x: any): MACDIndicator; 15 | stroke(): string | any; 16 | stroke(x: string | any): MACDIndicator; 17 | fill(): string | any; 18 | fill(x: string | any): MACDIndicator; 19 | echo(): any; 20 | echo(x: any): MACDIndicator; 21 | type(): string; 22 | type(x: string): MACDIndicator; 23 | merge(): any; 24 | merge(newMerge: any): MACDIndicator; 25 | options(): MACDOptions; 26 | options(newOptions: MACDOptions): MACDIndicator; 27 | } 28 | 29 | export default function () { 30 | const base = baseIndicator() 31 | .type(ALGORITHM_TYPE) 32 | .fill(appearanceOptions.fill) 33 | .stroke(appearanceOptions.stroke) 34 | .accessor((d: any) => d.macd); 35 | 36 | const underlyingAlgorithm = macd(); 37 | 38 | const mergedAlgorithm = merge() 39 | .algorithm(underlyingAlgorithm) 40 | .merge((datum: any, i: number) => { 41 | datum.macd = i; 42 | }); 43 | 44 | const indicator = (data: any[], options = { merge: true }) => { 45 | if (options.merge) { 46 | if (!base.accessor()) { 47 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 48 | } 49 | 50 | return mergedAlgorithm(data); 51 | } 52 | return underlyingAlgorithm(data); 53 | }; 54 | 55 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 56 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 57 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 58 | 59 | return indicator as MACDIndicator; 60 | } 61 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/pointAndFigure.ts: -------------------------------------------------------------------------------- 1 | import { rebind } from "../utils"; 2 | import { pointAndFigure } from "../calculator"; 3 | import baseIndicator from "./baseIndicator"; 4 | 5 | const ALGORITHM_TYPE = "PointAndFigure"; 6 | 7 | export default function () { 8 | const base = baseIndicator().type(ALGORITHM_TYPE); 9 | 10 | const underlyingAlgorithm = pointAndFigure(); 11 | 12 | const indicator = underlyingAlgorithm; 13 | 14 | rebind(indicator, base, "id", "stroke", "fill", "echo", "type"); 15 | rebind(indicator, underlyingAlgorithm, "dateAccessor", "dateMutator", "options"); 16 | 17 | return indicator; 18 | } 19 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/renko.ts: -------------------------------------------------------------------------------- 1 | import { rebind } from "../utils"; 2 | import { renko } from "../calculator"; 3 | import baseIndicator from "./baseIndicator"; 4 | 5 | const ALGORITHM_TYPE = "Renko"; 6 | 7 | export default function () { 8 | const base = baseIndicator().type(ALGORITHM_TYPE); 9 | 10 | const underlyingAlgorithm = renko(); 11 | 12 | const indicator = underlyingAlgorithm; 13 | 14 | rebind(indicator, base, "id", "stroke", "fill", "echo", "type"); 15 | rebind(indicator, underlyingAlgorithm, "options", "dateAccessor", "dateMutator"); 16 | 17 | return indicator; 18 | } 19 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/rsi.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { rsi } from "../calculator"; 3 | import { RSIOptions } from "../calculator/rsi"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "RSI"; 7 | 8 | interface RSIIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): RSIIndicator; 12 | accessor(): any; 13 | accessor(x: any): RSIIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): RSIIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): RSIIndicator; 18 | echo(): any; 19 | echo(x: any): RSIIndicator; 20 | type(): string; 21 | type(x: string): RSIIndicator; 22 | merge(): any; 23 | merge(newMerge: any): RSIIndicator; 24 | options(): RSIOptions; 25 | options(newOptions: RSIOptions): RSIIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.rsi); 32 | 33 | const underlyingAlgorithm = rsi(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.rsi = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | 50 | return underlyingAlgorithm(data); 51 | }; 52 | 53 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 54 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 55 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 56 | 57 | return indicator as RSIIndicator; 58 | } 59 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/sar.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { sar } from "../calculator"; 3 | import { SAROptions } from "../calculator/sar"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "SMA"; 7 | 8 | interface SARIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): SARIndicator; 12 | accessor(): any; 13 | accessor(x: any): SARIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): SARIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): SARIndicator; 18 | echo(): any; 19 | echo(x: any): SARIndicator; 20 | type(): string; 21 | type(x: string): SARIndicator; 22 | merge(): any; 23 | merge(newMerge: any): SARIndicator; 24 | options(): SAROptions; 25 | options(newOptions: SAROptions): SARIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.sar); 32 | 33 | const underlyingAlgorithm = sar(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.sar = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | return mergedAlgorithm(data); 47 | } 48 | return underlyingAlgorithm(data); 49 | }; 50 | 51 | rebind(indicator, base, "id", "accessor", "stroke", "echo", "type"); 52 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 53 | rebind(indicator, mergedAlgorithm, "merge"); 54 | 55 | return indicator as SARIndicator; 56 | } 57 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/sma.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { sma } from "../calculator"; 3 | import { SMAOptions } from "../calculator/sma"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "SMA"; 7 | 8 | interface SMAIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): SMAIndicator; 12 | accessor(): any; 13 | accessor(x: any): SMAIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): SMAIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): SMAIndicator; 18 | echo(): any; 19 | echo(x: any): SMAIndicator; 20 | type(): string; 21 | type(x: string): SMAIndicator; 22 | merge(): any; 23 | merge(newMerge: any): SMAIndicator; 24 | options(): SMAOptions; 25 | options(newOptions: SMAOptions): SMAIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.sma); 32 | 33 | const underlyingAlgorithm = sma(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.sma = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | 50 | return underlyingAlgorithm(data); 51 | }; 52 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 53 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 54 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 55 | 56 | return indicator as SMAIndicator; 57 | } 58 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/stochasticOscillator.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { sto } from "../calculator"; 3 | import { STOOptions } from "../calculator/sto"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "STO"; 7 | 8 | interface StochasticOscillatorIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): StochasticOscillatorIndicator; 12 | accessor(): any; 13 | accessor(x: any): StochasticOscillatorIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): StochasticOscillatorIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): StochasticOscillatorIndicator; 18 | echo(): any; 19 | echo(x: any): StochasticOscillatorIndicator; 20 | type(): string; 21 | type(x: string): StochasticOscillatorIndicator; 22 | merge(): any; 23 | merge(newMerge: any): StochasticOscillatorIndicator; 24 | options(): STOOptions; 25 | options(newOptions: STOOptions): StochasticOscillatorIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator().type(ALGORITHM_TYPE); 30 | 31 | const underlyingAlgorithm = sto(); 32 | 33 | const mergedAlgorithm = merge() 34 | .algorithm(underlyingAlgorithm) 35 | .merge((datum: any, i: number) => { 36 | datum.sto = i; 37 | }); 38 | 39 | const indicator = (data: any[], options = { merge: true }) => { 40 | if (options.merge) { 41 | if (!base.accessor()) { 42 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 43 | } 44 | 45 | return mergedAlgorithm(data); 46 | } 47 | 48 | return underlyingAlgorithm(data); 49 | }; 50 | 51 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 52 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 53 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 54 | 55 | return indicator as StochasticOscillatorIndicator; 56 | } 57 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/tma.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { tma } from "../calculator"; 3 | import { TMAOptions } from "../calculator/tma"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "TMA"; 7 | 8 | interface TMAIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): TMAIndicator; 12 | accessor(): any; 13 | accessor(x: any): TMAIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): TMAIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): TMAIndicator; 18 | echo(): any; 19 | echo(x: any): TMAIndicator; 20 | type(): string; 21 | type(x: string): TMAIndicator; 22 | merge(): any; 23 | merge(newMerge: any): TMAIndicator; 24 | options(): TMAOptions; 25 | options(newOptions: TMAOptions): TMAIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.tma); 32 | 33 | const underlyingAlgorithm = tma(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.tma = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | 50 | return underlyingAlgorithm(data); 51 | }; 52 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 53 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 54 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 55 | 56 | return indicator as TMAIndicator; 57 | } 58 | -------------------------------------------------------------------------------- /packages/indicators/src/indicator/wma.ts: -------------------------------------------------------------------------------- 1 | import { merge, rebind } from "../utils"; 2 | import { wma } from "../calculator"; 3 | import { WMAOptions } from "../calculator/wma"; 4 | import baseIndicator from "./baseIndicator"; 5 | 6 | const ALGORITHM_TYPE = "WMA"; 7 | 8 | interface WMAIndicator { 9 | (data: any[], options?: { merge: boolean }): any; 10 | id(): number; 11 | id(x: number): WMAIndicator; 12 | accessor(): any; 13 | accessor(x: any): WMAIndicator; 14 | stroke(): string | any; 15 | stroke(x: string | any): WMAIndicator; 16 | fill(): string | any; 17 | fill(x: string | any): WMAIndicator; 18 | echo(): any; 19 | echo(x: any): WMAIndicator; 20 | type(): string; 21 | type(x: string): WMAIndicator; 22 | merge(): any; 23 | merge(newMerge: any): WMAIndicator; 24 | options(): WMAOptions; 25 | options(newOptions: WMAOptions): WMAIndicator; 26 | } 27 | 28 | export default function () { 29 | const base = baseIndicator() 30 | .type(ALGORITHM_TYPE) 31 | .accessor((d: any) => d.wma); 32 | 33 | const underlyingAlgorithm = wma(); 34 | 35 | const mergedAlgorithm = merge() 36 | .algorithm(underlyingAlgorithm) 37 | .merge((datum: any, i: number) => { 38 | datum.wma = i; 39 | }); 40 | 41 | const indicator = (data: any[], options = { merge: true }) => { 42 | if (options.merge) { 43 | if (!base.accessor()) { 44 | throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); 45 | } 46 | 47 | return mergedAlgorithm(data); 48 | } 49 | 50 | return underlyingAlgorithm(data); 51 | }; 52 | 53 | rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); 54 | rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); 55 | rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); 56 | 57 | return indicator as WMAIndicator; 58 | } 59 | -------------------------------------------------------------------------------- /packages/indicators/src/utils/functor.ts: -------------------------------------------------------------------------------- 1 | export const functor = (v: any) => { 2 | return typeof v === "function" ? v : () => v; 3 | }; 4 | -------------------------------------------------------------------------------- /packages/indicators/src/utils/identity.ts: -------------------------------------------------------------------------------- 1 | export const identity = (d: T) => d; 2 | -------------------------------------------------------------------------------- /packages/indicators/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./functor"; 2 | export * from "./identity"; 3 | export { default as mappedSlidingWindow } from "./mappedSlidingWindow"; 4 | export { default as merge } from "./merge"; 5 | export * from "./path"; 6 | export { default as rebind } from "./rebind"; 7 | export { default as slidingWindow } from "./slidingWindow"; 8 | export { default as zipper } from "./zipper"; 9 | -------------------------------------------------------------------------------- /packages/indicators/src/utils/path.ts: -------------------------------------------------------------------------------- 1 | export const path = (loc: any | any[] = []) => { 2 | const key = Array.isArray(loc) ? loc : [loc]; 3 | const length = key.length; 4 | 5 | return function (obj: any, defaultValue?: any) { 6 | if (length === 0) { 7 | return obj !== undefined && obj !== null ? obj : defaultValue; 8 | } 9 | 10 | let index = 0; 11 | while (obj != null && index < length) { 12 | obj = obj[key[index++]]; 13 | } 14 | return index === length ? obj : defaultValue; 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/indicators/src/utils/rebind.ts: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/d3fc/d3fc-rebind/blob/master/src/rebind.js 2 | 3 | function createReboundMethod(target: any, source: any, name: string) { 4 | const method = source[name]; 5 | if (typeof method !== "function") { 6 | throw new Error(`Attempt to rebind ${name} which isn't a function on the source object`); 7 | } 8 | return (...args: any[]) => { 9 | const value = method.apply(source, args); 10 | return value === source ? target : value; 11 | }; 12 | } 13 | 14 | export default function rebind(target: any, source: any, ...names: string[]) { 15 | for (const name of names) { 16 | target[name] = createReboundMethod(target, source, name); 17 | } 18 | return target; 19 | } 20 | -------------------------------------------------------------------------------- /packages/indicators/src/utils/zipper.ts: -------------------------------------------------------------------------------- 1 | /* an extension to d3.zip so we call a function instead of an array */ 2 | 3 | import { min } from "d3-array"; 4 | import { identity } from "./identity"; 5 | 6 | interface Zip { 7 | (...args: any[]): any[]; 8 | combine(): any; 9 | combine(x: any): Zip; 10 | } 11 | 12 | export default function zipper() { 13 | let combine = identity; 14 | 15 | function zip() { 16 | const n = arguments.length; 17 | if (n === 0) { 18 | return []; 19 | } 20 | const m = min(arguments, d3_zipLength) ?? 0; 21 | 22 | const zips = new Array(m); 23 | for (let i = -1; ++i < m; ) { 24 | // tslint:disable-next-line: no-shadowed-variable 25 | for (let j = -1, zip = (zips[i] = new Array(n)); ++j < n; ) { 26 | zip[j] = arguments[j][i]; 27 | } 28 | 29 | // @ts-ignore 30 | zips[i] = combine.apply(this, zips[i]); 31 | } 32 | return zips; 33 | } 34 | function d3_zipLength(d: any[]) { 35 | return d.length; 36 | } 37 | zip.combine = function (x: any) { 38 | if (!arguments.length) { 39 | return combine; 40 | } 41 | combine = x; 42 | return zip; 43 | }; 44 | return zip as Zip; 45 | } 46 | -------------------------------------------------------------------------------- /packages/indicators/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/interactive/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/interactive/README.md: -------------------------------------------------------------------------------- 1 | # Interactive 2 | 3 | ```bash 4 | npm i @react-financial-charts/interactive 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/interactive/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/interactive", 3 | "version": "2.0.0", 4 | "description": "Interactive features for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "@react-financial-charts/coordinates": "file:../coordinates", 42 | "@react-financial-charts/core": "file:../core", 43 | "d3-array": "^2.9.1", 44 | "d3-format": "^2.0.0", 45 | "d3-interpolate": "^2.0.1", 46 | "d3-path": "^2.0.0" 47 | }, 48 | "peerDependencies": { 49 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 50 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/interactive/src/ClickCallback.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { getMouseCanvas, GenericChartComponent } from "@react-financial-charts/core"; 3 | 4 | interface ClickCallbackProps { 5 | readonly disablePan: boolean; 6 | readonly onMouseDown?: (e: React.MouseEvent, moreProps: any) => void; 7 | readonly onClick?: (e: React.MouseEvent, moreProps: any) => void; 8 | readonly onDoubleClick?: (e: React.MouseEvent, moreProps: any) => void; 9 | readonly onContextMenu?: (e: React.MouseEvent, moreProps: any) => void; 10 | readonly onMouseMove?: (e: React.MouseEvent, moreProps: any) => void; 11 | readonly onPan?: (e: React.MouseEvent, moreProps: any) => void; 12 | readonly onPanEnd?: (e: React.MouseEvent, moreProps: any) => void; 13 | } 14 | 15 | export class ClickCallback extends React.Component { 16 | public static defaultProps = { 17 | disablePan: false, 18 | }; 19 | 20 | public render() { 21 | const { onMouseDown, onClick, onDoubleClick, onContextMenu, onMouseMove, onPan, onPanEnd } = this.props; 22 | 23 | return ( 24 | 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/interactive/src/components/Text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { getMouseCanvas, GenericChartComponent } from "@react-financial-charts/core"; 3 | 4 | export interface TextProps { 5 | readonly children: string; 6 | readonly fontFamily: string; 7 | readonly fontSize: number; 8 | readonly fillStyle: string; 9 | readonly selected?: boolean; 10 | readonly xyProvider: (moreProps: any) => number[]; 11 | } 12 | 13 | export class Text extends React.Component { 14 | public static defaultProps = { 15 | selected: false, 16 | }; 17 | 18 | public render() { 19 | const { selected } = this.props; 20 | 21 | return ( 22 | 29 | ); 30 | } 31 | 32 | private readonly isHover = () => { 33 | return false; 34 | }; 35 | 36 | private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => { 37 | const { xyProvider, fontFamily, fontSize, fillStyle, children } = this.props; 38 | 39 | const [x, y] = xyProvider(moreProps); 40 | 41 | ctx.font = `${fontSize}px ${fontFamily}`; 42 | ctx.fillStyle = fillStyle; 43 | 44 | ctx.beginPath(); 45 | ctx.fillText(children, x, y); 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /packages/interactive/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ChannelWithArea"; 2 | export * from "./ClickableCircle"; 3 | export * from "./ClickableShape"; 4 | export * from "./GannFan"; 5 | export * from "./HoverTextNearMouse"; 6 | export * from "./InteractiveStraightLine"; 7 | export * from "./InteractiveText"; 8 | export * from "./InteractiveYCoordinate"; 9 | export * from "./LinearRegressionChannelWithArea"; 10 | export * from "./MouseLocationIndicator"; 11 | export * from "./Text"; 12 | -------------------------------------------------------------------------------- /packages/interactive/src/index.ts: -------------------------------------------------------------------------------- 1 | export { TrendLine } from "./TrendLine"; 2 | export { FibonacciRetracement } from "./FibonacciRetracement"; 3 | export { EquidistantChannel } from "./EquidistantChannel"; 4 | export { StandardDeviationChannel } from "./StandardDeviationChannel"; 5 | export { GannFan } from "./GannFan"; 6 | export { ClickCallback } from "./ClickCallback"; 7 | export { Brush } from "./Brush"; 8 | export { InteractiveText } from "./InteractiveText"; 9 | export { InteractiveYCoordinate } from "./InteractiveYCoordinate"; 10 | export { DrawingObjectSelector } from "./DrawingObjectSelector"; 11 | export { ZoomButtons } from "./ZoomButtons"; 12 | export * from "./utils"; 13 | -------------------------------------------------------------------------------- /packages/interactive/src/wrapper/index.ts: -------------------------------------------------------------------------------- 1 | export { EachEquidistantChannel } from "./EachEquidistantChannel"; 2 | export { EachFibRetracement } from "./EachFibRetracement"; 3 | export { EachGannFan } from "./EachGannFan"; 4 | export { EachInteractiveYCoordinate } from "./EachInteractiveYCoordinate"; 5 | export { EachLinearRegressionChannel } from "./EachLinearRegressionChannel"; 6 | export { EachText } from "./EachText"; 7 | export { EachTrendLine } from "./EachTrendLine"; 8 | -------------------------------------------------------------------------------- /packages/interactive/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/scales/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/scales/README.md: -------------------------------------------------------------------------------- 1 | # Axes 2 | 3 | ```bash 4 | npm i @react-financial-charts/axes 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/scales/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/scales", 3 | "version": "2.0.0", 4 | "description": "Scales for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "@react-financial-charts/core": "file:../core", 42 | "d3-array": "^2.9.1", 43 | "d3-scale": "^3.2.3", 44 | "d3-time-format": "^3.0.0" 45 | }, 46 | "peerDependencies": { 47 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 48 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/scales/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ScaleContinuousNumeric, ScaleTime } from "d3-scale"; 2 | export { 3 | default as discontinuousTimeScaleProvider, 4 | discontinuousTimeScaleProviderBuilder, 5 | } from "./discontinuousTimeScaleProvider"; 6 | export { default as financeDiscontinuousScale } from "./financeDiscontinuousScale"; 7 | export * from "./timeFormat"; 8 | 9 | export const defaultScaleProvider = ( 10 | xScale: ScaleContinuousNumeric | ScaleTime, 11 | ) => { 12 | return (data: TData[], xAccessor: (data: TData) => TXAxis) => ({ 13 | data, 14 | xScale, 15 | xAccessor, 16 | displayXAccessor: xAccessor, 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/scales/src/timeFormat.ts: -------------------------------------------------------------------------------- 1 | import { timeSecond, timeMinute, timeHour, timeDay, timeWeek, timeMonth, timeYear } from "d3-time"; 2 | import { timeFormat as d3TimeFormat } from "d3-time-format"; 3 | 4 | const formatMillisecond = d3TimeFormat(".%L"); 5 | const formatSecond = d3TimeFormat(":%S"); 6 | const formatMinute = d3TimeFormat("%H:%M"); 7 | const formatHour = d3TimeFormat("%H:%M"); 8 | const formatDay = d3TimeFormat("%e"); 9 | const formatWeek = d3TimeFormat("%e"); 10 | const formatMonth = d3TimeFormat("%b"); 11 | const formatYear = d3TimeFormat("%Y"); 12 | 13 | export const timeFormat = (date: Date) => { 14 | return ( 15 | timeSecond(date) < date 16 | ? formatMillisecond 17 | : timeMinute(date) < date 18 | ? formatSecond 19 | : timeHour(date) < date 20 | ? formatMinute 21 | : timeDay(date) < date 22 | ? formatHour 23 | : timeMonth(date) < date 24 | ? timeWeek(date) < date 25 | ? formatDay 26 | : formatWeek 27 | : timeYear(date) < date 28 | ? formatMonth 29 | : formatYear 30 | )(date); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/scales/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/series/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/series/README.md: -------------------------------------------------------------------------------- 1 | # Series 2 | 3 | ```bash 4 | npm i @react-financial-charts/series 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/series/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/series", 3 | "version": "2.0.0", 4 | "description": "Series for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "@react-financial-charts/core": "file:../core", 42 | "@types/d3-scale": "^3.2.2", 43 | "d3-array": "^2.9.1", 44 | "d3-scale": "^3.2.3", 45 | "d3-shape": "^2.0.0" 46 | }, 47 | "peerDependencies": { 48 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 49 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/series/src/AlternateDataSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { ChartCanvasContext } from "@react-financial-charts/core"; 3 | 4 | export interface AlternateDataSeriesProps { 5 | readonly data: TData[]; 6 | } 7 | 8 | export const AlternateDataSeries = ({ 9 | data, 10 | children, 11 | }: React.PropsWithChildren>) => { 12 | const context = React.useContext(ChartCanvasContext); 13 | const contextValue = React.useMemo(() => { 14 | const { plotData, xAccessor } = context; 15 | 16 | const startDate = xAccessor(plotData[0]); 17 | const endDate = xAccessor(plotData[plotData.length - 1]); 18 | 19 | return { 20 | ...context, 21 | plotData: data.filter((d) => { 22 | const date = xAccessor(d); 23 | return date > startDate && date < endDate; 24 | }), 25 | }; 26 | }, [data, context]); 27 | 28 | return {children}; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/series/src/GroupedBarSeries.tsx: -------------------------------------------------------------------------------- 1 | import { getAxisCanvas, GenericChartComponent } from "@react-financial-charts/core"; 2 | import { ScaleContinuousNumeric, ScaleTime } from "d3-scale"; 3 | import * as React from "react"; 4 | import { drawOnCanvasHelper, identityStack, StackedBarSeries } from "./StackedBarSeries"; 5 | 6 | export interface GroupedBarSeriesProps { 7 | readonly baseAt: 8 | | number 9 | | (( 10 | xScale: ScaleContinuousNumeric | ScaleTime, 11 | yScale: ScaleContinuousNumeric, 12 | datum: any, 13 | ) => number); 14 | readonly direction: "up" | "down"; 15 | readonly fillStyle?: string | ((data: any) => string); 16 | readonly spaceBetweenBar?: number; 17 | readonly stroke: boolean; 18 | readonly widthRatio?: number; 19 | readonly yAccessor: ((data: any) => number | undefined) | ((d: any) => number)[]; 20 | } 21 | 22 | export class GroupedBarSeries extends React.Component { 23 | public static defaultProps = { 24 | ...StackedBarSeries.defaultProps, 25 | spaceBetweenBar: 5, 26 | widthRatio: 0.8, 27 | }; 28 | 29 | public render() { 30 | return ; 31 | } 32 | 33 | private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => { 34 | const { xAccessor } = moreProps; 35 | 36 | drawOnCanvasHelper(ctx, this.props, moreProps, xAccessor, identityStack, this.postProcessor); 37 | }; 38 | 39 | private readonly postProcessor = (array: any[]) => { 40 | return array.map((each) => { 41 | return { 42 | ...each, 43 | x: each.x + each.offset - each.groupOffset, 44 | width: each.groupWidth, 45 | }; 46 | }); 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /packages/series/src/SVGComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { GenericChartComponent } from "@react-financial-charts/core"; 3 | 4 | interface SVGComponentProps { 5 | readonly children: (moreProps: any) => React.ReactNode; 6 | } 7 | 8 | export class SVGComponent extends React.Component { 9 | public render() { 10 | const { children } = this.props; 11 | return ; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/series/src/StochasticSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { LineSeries } from "./LineSeries"; 4 | import { StraightLine } from "./StraightLine"; 5 | 6 | export interface StochasticSeriesProps { 7 | readonly className?: string; 8 | readonly overBought?: number; 9 | readonly overSold?: number; 10 | readonly middle?: number; 11 | readonly strokeStyle?: { 12 | top: string; 13 | middle: string; 14 | bottom: string; 15 | dLine: string; 16 | kLine: string; 17 | }; 18 | readonly yAccessor: (data: any) => { K: number; D: number }; 19 | } 20 | 21 | /** 22 | * The Stochastic Oscillator is a momentum indicator that shows the location of the close relative to the high-low range over a set number of periods. 23 | */ 24 | export class StochasticSeries extends React.Component { 25 | public static defaultProps = { 26 | className: "react-financial-charts-stochastic-series", 27 | strokeStyle: { 28 | top: "rgba(150, 75, 0, 0.3)", 29 | middle: "rgba(0, 0, 0, 0.3)", 30 | bottom: "rgba(150, 75, 0, 0.3)", 31 | dLine: "#EA2BFF", 32 | kLine: "#74D400", 33 | }, 34 | overSold: 80, 35 | middle: 50, 36 | overBought: 20, 37 | }; 38 | 39 | public render() { 40 | const { 41 | className, 42 | strokeStyle = StochasticSeries.defaultProps.strokeStyle, 43 | overSold, 44 | middle, 45 | overBought, 46 | } = this.props; 47 | 48 | return ( 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | ); 57 | } 58 | 59 | private readonly yAccessorForK = (d: any) => { 60 | const { yAccessor } = this.props; 61 | 62 | return yAccessor(d) && yAccessor(d).K; 63 | }; 64 | 65 | private readonly yAccessorForD = (d: any) => { 66 | const { yAccessor } = this.props; 67 | 68 | return yAccessor(d) && yAccessor(d).D; 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /packages/series/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./AlternateDataSeries"; 2 | export * from "./AlternatingFillAreaSeries"; 3 | export * from "./AreaOnlySeries"; 4 | export * from "./AreaSeries"; 5 | export * from "./markers"; 6 | export * from "./LineSeries"; 7 | export * from "./CandlestickSeries"; 8 | export * from "./OHLCSeries"; 9 | export * from "./BarSeries"; 10 | export { StackedBarSeries, StackedBarSeriesProps } from "./StackedBarSeries"; 11 | export * from "./GroupedBarSeries"; 12 | export * from "./KagiSeries"; 13 | export * from "./PointAndFigureSeries"; 14 | export * from "./RenkoSeries"; 15 | export * from "./MACDSeries"; 16 | export * from "./BollingerSeries"; 17 | export * from "./RSISeries"; 18 | export * from "./StochasticSeries"; 19 | export * from "./ElderRaySeries"; 20 | export * from "./VolumeProfileSeries"; 21 | export * from "./ScatterSeries"; 22 | export * from "./StraightLine"; 23 | export * from "./SARSeries"; 24 | -------------------------------------------------------------------------------- /packages/series/src/markers/CircleMarker.tsx: -------------------------------------------------------------------------------- 1 | import { functor } from "@react-financial-charts/core"; 2 | import * as React from "react"; 3 | 4 | export interface CircleMarkerProps { 5 | readonly className?: string; 6 | readonly fillStyle?: string; 7 | readonly point: { 8 | x: number; 9 | y: number; 10 | datum: any; 11 | }; 12 | readonly r: number | ((datum: any) => number); 13 | readonly strokeStyle?: string; 14 | readonly strokeWidth?: number; 15 | } 16 | 17 | export class CircleMarker extends React.Component { 18 | public static defaultProps = { 19 | fillStyle: "#4682B4", 20 | className: "react-financial-charts-marker-circle", 21 | }; 22 | 23 | public static drawOnCanvas = ( 24 | props: CircleMarkerProps, 25 | point: { x: number; y: number; datum: unknown }, 26 | ctx: CanvasRenderingContext2D, 27 | ) => { 28 | const { strokeStyle, fillStyle, r, strokeWidth } = props; 29 | 30 | if (strokeStyle !== undefined) { 31 | ctx.strokeStyle = strokeStyle; 32 | } 33 | if (strokeWidth !== undefined) { 34 | ctx.lineWidth = strokeWidth; 35 | } 36 | if (fillStyle !== undefined) { 37 | ctx.fillStyle = fillStyle; 38 | } 39 | 40 | const { datum, x, y } = point; 41 | 42 | const radius = functor(r)(datum); 43 | 44 | ctx.moveTo(x, y); 45 | ctx.beginPath(); 46 | ctx.arc(x, y, radius, 0, 2 * Math.PI, false); 47 | ctx.fill(); 48 | if (strokeStyle !== undefined) { 49 | ctx.stroke(); 50 | } 51 | }; 52 | 53 | public render() { 54 | const { className, strokeStyle, strokeWidth, fillStyle, point, r } = this.props; 55 | const radius = functor(r)(point.datum); 56 | 57 | return ( 58 | 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/series/src/markers/SquareMarker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { functor } from "@react-financial-charts/core"; 3 | 4 | export interface SquareProps { 5 | readonly className?: string; 6 | readonly fillStyle?: string; 7 | readonly point: { 8 | x: number; 9 | y: number; 10 | datum: any; 11 | }; 12 | readonly strokeStyle?: string; 13 | readonly strokeWidth?: number; 14 | readonly width: number | ((datum: any) => number); 15 | } 16 | 17 | export class Square extends React.Component { 18 | public static defaultProps = { 19 | fillStyle: "#4682B4", 20 | className: "react-financial-charts-marker-rect", 21 | }; 22 | 23 | public static drawOnCanvas = ( 24 | props: SquareProps, 25 | point: { x: number; y: number; datum: unknown }, 26 | ctx: CanvasRenderingContext2D, 27 | ) => { 28 | const { strokeStyle, fillStyle, strokeWidth, width } = props; 29 | 30 | if (strokeStyle !== undefined) { 31 | ctx.strokeStyle = strokeStyle; 32 | } 33 | if (strokeWidth !== undefined) { 34 | ctx.lineWidth = strokeWidth; 35 | } 36 | if (fillStyle !== undefined) { 37 | ctx.fillStyle = fillStyle; 38 | } 39 | 40 | const w = functor(width)(point.datum); 41 | const x = point.x - w / 2; 42 | const y = point.y - w / 2; 43 | ctx.beginPath(); 44 | ctx.rect(x, y, w, w); 45 | ctx.fill(); 46 | 47 | if (strokeStyle !== undefined) { 48 | ctx.stroke(); 49 | } 50 | }; 51 | 52 | public render() { 53 | const { className, strokeStyle, strokeWidth, fillStyle, point, width } = this.props; 54 | const w = functor(width)(point.datum); 55 | const x = point.x - w / 2; 56 | const y = point.y - w / 2; 57 | 58 | return ( 59 | 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/series/src/markers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./CircleMarker"; 2 | export * from "./SquareMarker"; 3 | export * from "./TriangleMarker"; 4 | -------------------------------------------------------------------------------- /packages/series/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/stories/.storybook/main.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@storybook/react/types').StorybookConfig} */ 2 | module.exports = { 3 | addons: ["@storybook/addon-essentials"], 4 | stories: ["../src/**/*.stories.(ts|tsx|mdx)"], 5 | webpackFinal: async (config) => { 6 | config.module.rules.push({ 7 | test: /\.(js|map)$/, 8 | use: "source-map-loader", 9 | enforce: "pre", 10 | }); 11 | 12 | return config; 13 | }, 14 | reactOptions: { 15 | strictMode: true, 16 | fastRefresh: true, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/stories/.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from "@storybook/addons"; 2 | import { create } from "@storybook/theming"; 3 | 4 | addons.setConfig({ 5 | theme: create({ 6 | base: "light", 7 | brandTitle: "React Financial Charts", 8 | brandUrl: "https://github.com/reactivemarkets/react-financial-charts", 9 | }), 10 | }); 11 | -------------------------------------------------------------------------------- /packages/stories/.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /packages/stories/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | export const parameters = { 2 | controls: { hideNoControlsWarning: true }, 3 | options: { 4 | storySort: { 5 | order: ['Intro', 'Features', 'Visualization'], 6 | }, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/stories/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/stories/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/stories", 3 | "version": "2.0.1", 4 | "description": "Stories for react financial charts", 5 | "private": true, 6 | "author": "Reactive Markets", 7 | "license": "MIT", 8 | "scripts": { 9 | "clean": "rimraf ../docs lib", 10 | "docs": "build-storybook -c .storybook -o ../../docs", 11 | "start": "start-storybook -p 4444" 12 | }, 13 | "dependencies": { 14 | "d3-dsv": "^2.0.0", 15 | "d3-format": "^2.0.0", 16 | "d3-time-format": "^3.0.0", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "react-financial-charts": "file:../charts" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/stories/src/Intro.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Canvas, Meta } from "@storybook/addon-docs/blocks"; 2 | import StockChart from "./features/StockChart"; 3 | 4 | 5 | 6 | # React Financial Charts 7 | 8 | Charts dedicated to finance. 9 | 10 | ## Features 11 | 12 | - integrates multiple chart types 13 | - technical indicators and overlays 14 | - drawing objects 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/stories/src/data/iOHLCData.ts: -------------------------------------------------------------------------------- 1 | export interface IOHLCData { 2 | readonly close: number; 3 | readonly date: Date; 4 | readonly high: number; 5 | readonly low: number; 6 | readonly open: number; 7 | readonly volume: number; 8 | } 9 | -------------------------------------------------------------------------------- /packages/stories/src/data/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./iOHLCData"; 2 | export * from "./withOHLCData"; 3 | export * from "./withUpdatingData"; 4 | -------------------------------------------------------------------------------- /packages/stories/src/data/withOHLCData.tsx: -------------------------------------------------------------------------------- 1 | import { tsvParse } from "d3-dsv"; 2 | import { timeParse } from "d3-time-format"; 3 | import * as React from "react"; 4 | import { IOHLCData } from "./iOHLCData"; 5 | 6 | const parseDate = timeParse("%Y-%m-%d"); 7 | 8 | const parseData = () => { 9 | return (d: any) => { 10 | const date = parseDate(d.date); 11 | if (date === null) { 12 | d.date = new Date(Number(d.date)); 13 | } else { 14 | d.date = new Date(date); 15 | } 16 | 17 | for (const key in d) { 18 | if (key !== "date" && Object.prototype.hasOwnProperty.call(d, key)) { 19 | d[key] = +d[key]; 20 | } 21 | } 22 | 23 | return d as IOHLCData; 24 | }; 25 | }; 26 | 27 | interface WithOHLCDataProps { 28 | readonly data: IOHLCData[]; 29 | } 30 | 31 | interface WithOHLCState { 32 | data?: IOHLCData[]; 33 | message: string; 34 | } 35 | 36 | export function withOHLCData(dataSet = "DAILY") { 37 | return (OriginalComponent: React.ComponentClass) => { 38 | return class WithOHLCData extends React.Component, WithOHLCState> { 39 | public constructor(props: Omit) { 40 | super(props); 41 | 42 | this.state = { 43 | message: `Loading ${dataSet} data...`, 44 | }; 45 | } 46 | 47 | public componentDidMount() { 48 | fetch( 49 | `https://raw.githubusercontent.com/reactivemarkets/react-financial-charts/master/packages/stories/src/data/${dataSet}.tsv`, 50 | ) 51 | .then((response) => response.text()) 52 | .then((data) => tsvParse(data, parseData())) 53 | .then((data) => { 54 | this.setState({ 55 | data, 56 | }); 57 | }) 58 | .catch(() => { 59 | this.setState({ 60 | message: `Failed to fetch data.`, 61 | }); 62 | }); 63 | } 64 | 65 | public render() { 66 | const { data, message } = this.state; 67 | if (data === undefined) { 68 | return
{message}
; 69 | } 70 | 71 | return ; 72 | } 73 | }; 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /packages/stories/src/data/withUpdatingData.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { IOHLCData } from "./iOHLCData"; 3 | 4 | interface WithOHLCDataProps { 5 | readonly data: IOHLCData[]; 6 | } 7 | 8 | interface WithOHLCState { 9 | data: IOHLCData[]; 10 | length: number; 11 | } 12 | 13 | export function withUpdatingData(initialLength = 120, interval = 1_000) { 14 | return (OriginalComponent: React.ComponentClass) => { 15 | return class WithOHLCData extends React.Component { 16 | public interval?: number; 17 | 18 | public constructor(props: TProps) { 19 | super(props); 20 | 21 | this.state = { 22 | data: props.data.slice(0, initialLength), 23 | length: initialLength, 24 | }; 25 | } 26 | 27 | public componentDidMount() { 28 | this.interval = window.setInterval(() => { 29 | const { data } = this.props; 30 | const { length } = this.state; 31 | 32 | if (length < data.length) { 33 | this.setState({ 34 | data: data.slice(0, length + 1), 35 | length: length + 1, 36 | }); 37 | } else { 38 | window.clearInterval(this.interval); 39 | } 40 | }, interval); 41 | } 42 | 43 | public componentWillUnmount() { 44 | window.clearInterval(this.interval); 45 | } 46 | 47 | public render() { 48 | const { data } = this.state; 49 | 50 | return ; 51 | } 52 | }; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /packages/stories/src/features/FullCanvas.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import StockChart, { MinutesStockChart, SecondsStockChart } from "./StockChart"; 3 | 4 | export default { 5 | component: StockChart, 6 | title: "Features/Full Screen", 7 | }; 8 | 9 | export const daily = () => ; 10 | 11 | export const minutes = () => ; 12 | 13 | export const seconds = () => ; 14 | -------------------------------------------------------------------------------- /packages/stories/src/features/annotated/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Annotate } from "@react-financial-charts/annotations"; 3 | import Annotated from "./Annotated"; 4 | 5 | export default { 6 | component: Annotate, 7 | title: "Features/Annotate", 8 | }; 9 | 10 | export const labels = () => ; 11 | 12 | export const paths = () => ; 13 | -------------------------------------------------------------------------------- /packages/stories/src/features/annotations/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { Label, LabelProps } from "../../../../annotations/src/Label"; 4 | import Annotations from "./Annotations"; 5 | 6 | export default { 7 | component: Label, 8 | title: "Features/Annotations", 9 | argTypes: { 10 | fillStyle: { control: "color" }, 11 | text: { 12 | control: { 13 | type: "text", 14 | }, 15 | }, 16 | }, 17 | }; 18 | 19 | const Template: Story = (args) => ; 20 | 21 | export const background = Template.bind({}); 22 | -------------------------------------------------------------------------------- /packages/stories/src/features/axis/Axis.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | discontinuousTimeScaleProviderBuilder, 4 | CandlestickSeries, 5 | Chart, 6 | ChartCanvas, 7 | XAxis, 8 | YAxis, 9 | YAxisProps, 10 | withDeviceRatio, 11 | withSize, 12 | } from "react-financial-charts"; 13 | import { IOHLCData, withOHLCData } from "../../data"; 14 | 15 | interface ChartProps extends Partial { 16 | readonly data: IOHLCData[]; 17 | readonly height: number; 18 | readonly ratio: number; 19 | readonly width: number; 20 | } 21 | 22 | class AxisExample extends React.Component { 23 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 24 | (d: IOHLCData) => d.date, 25 | ); 26 | 27 | public render() { 28 | const { axisAt = "right", data: initialData, height, ratio, width, ...rest } = this.props; 29 | 30 | const margin = { 31 | bottom: 24, 32 | left: axisAt === "left" ? 48 : 0, 33 | right: axisAt === "right" ? 48 : 0, 34 | top: 0, 35 | }; 36 | 37 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(initialData); 38 | 39 | const max = xAccessor(data[data.length - 1]); 40 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 41 | const xExtents = [min, max]; 42 | 43 | return ( 44 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ); 63 | } 64 | 65 | private readonly yExtents = (data: IOHLCData) => { 66 | return [data.high, data.low]; 67 | }; 68 | } 69 | 70 | export default withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(AxisExample))); 71 | -------------------------------------------------------------------------------- /packages/stories/src/features/axis/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { YAxis, YAxisProps } from "../../../../axes/src/YAxis"; 4 | import AxisExample from "./Axis"; 5 | 6 | export default { 7 | component: YAxis, 8 | title: "Features/Axis", 9 | argTypes: { 10 | axisAt: { 11 | control: { 12 | type: "select", 13 | options: ["left", "right", "middle"], 14 | }, 15 | }, 16 | gridLinesStrokeStyle: { control: "color" }, 17 | strokeStyle: { control: "color" }, 18 | tickLabelFill: { control: "color" }, 19 | tickStrokeStyle: { control: "color" }, 20 | }, 21 | }; 22 | 23 | const Template: Story = (args) => ; 24 | 25 | export const yAxis = Template.bind({}); 26 | -------------------------------------------------------------------------------- /packages/stories/src/features/coordinates/Coordinates.tsx: -------------------------------------------------------------------------------- 1 | import { format } from "d3-format"; 2 | import * as React from "react"; 3 | import { 4 | discontinuousTimeScaleProviderBuilder, 5 | CandlestickSeries, 6 | Chart, 7 | ChartCanvas, 8 | MouseCoordinateY, 9 | XAxis, 10 | YAxis, 11 | withDeviceRatio, 12 | withSize, 13 | } from "react-financial-charts"; 14 | import { IOHLCData, withOHLCData } from "../../data"; 15 | 16 | interface ChartProps { 17 | readonly arrowWidth?: number; 18 | readonly data: IOHLCData[]; 19 | readonly height: number; 20 | readonly ratio: number; 21 | readonly width: number; 22 | } 23 | 24 | class Coordinates extends React.Component { 25 | private readonly margin = { left: 0, right: 48, top: 0, bottom: 24 }; 26 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 27 | (d: IOHLCData) => d.date, 28 | ); 29 | private readonly pricesDisplayFormat = format(".2f"); 30 | 31 | public render() { 32 | const { arrowWidth, data: initialData, height, ratio, width } = this.props; 33 | 34 | const { margin, xScaleProvider } = this; 35 | 36 | const { data, xScale, xAccessor, displayXAccessor } = xScaleProvider(initialData); 37 | 38 | const max = xAccessor(data[data.length - 1]); 39 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 40 | const xExtents = [min, max]; 41 | 42 | return ( 43 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ); 63 | } 64 | 65 | private readonly yExtents = (data: IOHLCData) => { 66 | return [data.high, data.low]; 67 | }; 68 | } 69 | 70 | export default withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(Coordinates))); 71 | -------------------------------------------------------------------------------- /packages/stories/src/features/coordinates/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { MouseCoordinateY } from "@react-financial-charts/coordinates"; 3 | import Coordinates from "./Coordinates"; 4 | 5 | export default { 6 | component: MouseCoordinateY, 7 | title: "Features/Coordinates", 8 | }; 9 | 10 | export const edge = () => ; 11 | 12 | export const arrows = () => ; 13 | -------------------------------------------------------------------------------- /packages/stories/src/features/cursors/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { Cursor, CursorProps } from "../../../../coordinates/src/Cursor"; 4 | import Cursors from "./Cursors"; 5 | 6 | export default { 7 | component: Cursor, 8 | title: "Features/Cursors", 9 | argTypes: { 10 | strokeStyle: { control: "color" }, 11 | xCursorShapeFillStyle: { control: "color" }, 12 | xCursorShapeStrokeStyle: { control: "color" }, 13 | }, 14 | }; 15 | 16 | const Template: Story = (args) => ; 17 | 18 | export const cursor = Template.bind({}); 19 | 20 | export const crosshair = () => ; 21 | -------------------------------------------------------------------------------- /packages/stories/src/features/edgeCases/BasicLineSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Chart, 4 | ChartCanvas, 5 | XAxis, 6 | YAxis, 7 | discontinuousTimeScaleProviderBuilder, 8 | LineSeries, 9 | withDeviceRatio, 10 | withSize, 11 | } from "react-financial-charts"; 12 | import { IOHLCData } from "../../data"; 13 | 14 | interface ChartProps { 15 | readonly data: IOHLCData[]; 16 | readonly height: number; 17 | readonly width: number; 18 | readonly ratio: number; 19 | } 20 | 21 | class BasicLineSeries extends React.Component { 22 | private readonly margin = { left: 0, right: 40, top: 0, bottom: 24 }; 23 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 24 | (d: IOHLCData) => d.date, 25 | ); 26 | 27 | public render() { 28 | const { data: initialData, height, ratio, width } = this.props; 29 | 30 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(initialData); 31 | 32 | return ( 33 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | } 53 | 54 | private readonly yAccessor = (data: IOHLCData) => { 55 | return data.close; 56 | }; 57 | 58 | private readonly yExtents = (data: IOHLCData) => { 59 | return [data.low, data.high]; 60 | }; 61 | } 62 | 63 | export default withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicLineSeries)); 64 | -------------------------------------------------------------------------------- /packages/stories/src/features/edgeCases/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import BasicLineSeries from "./BasicLineSeries"; 3 | 4 | export default { 5 | title: "Features/EdgeCases", 6 | }; 7 | 8 | export const noData = () => ; 9 | 10 | export const singleDataPoint = () => ( 11 | 12 | ); 13 | 14 | export const twoDataPoint = () => ( 15 | 21 | ); 22 | 23 | export const threeDataPoint = () => ( 24 | 31 | ); 32 | 33 | export const emptyThenThreeAsync = () => { 34 | const [data, setData] = useState([]); 35 | useEffect(() => { 36 | const timeout = setTimeout(() => { 37 | console.log("Set data"); 38 | setData([ 39 | { 40 | close: 120, 41 | open: 120, 42 | high: 140, 43 | low: 100, 44 | date: new Date(2020, 7, 8, 10, 0, 0, 0), 45 | volume: 1_000_000, 46 | }, 47 | { 48 | close: 140, 49 | open: 120, 50 | high: 150, 51 | low: 100, 52 | date: new Date(2020, 7, 8, 10, 1, 0, 0), 53 | volume: 1_000_000, 54 | }, 55 | { 56 | close: 120, 57 | open: 120, 58 | high: 140, 59 | low: 100, 60 | date: new Date(2020, 7, 8, 10, 2, 0, 0), 61 | volume: 1_000_000, 62 | }, 63 | ]); 64 | }, 1000); 65 | return () => clearTimeout(timeout); 66 | }, []); 67 | return ; 68 | }; 69 | -------------------------------------------------------------------------------- /packages/stories/src/features/interaction/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | lastVisibleItemBasedZoomAnchor, 4 | mouseBasedZoomAnchor, 5 | rightDomainBasedZoomAnchor, 6 | } from "@react-financial-charts/core"; 7 | import { ChartCanvas } from "../../../../core/src/ChartCanvas"; 8 | import Interaction from "./Interaction"; 9 | 10 | export default { 11 | component: ChartCanvas, 12 | title: "Features/Interaction", 13 | }; 14 | 15 | export const clamp = () => ; 16 | 17 | export const disable = () => ; 18 | 19 | export const disablePan = () => ; 20 | 21 | export const disableZoom = () => ; 22 | 23 | export const zoomAnchorToMouse = () => ; 24 | 25 | export const zoomAnchorToLastVisible = () => ; 26 | 27 | export const zoomAnchorToBounds = () => ; 28 | -------------------------------------------------------------------------------- /packages/stories/src/features/scales/Scales.tsx: -------------------------------------------------------------------------------- 1 | import { format } from "d3-format"; 2 | import { scaleTime, ScaleTime, ScaleContinuousNumeric } from "d3-scale"; 3 | import * as React from "react"; 4 | import { 5 | Chart, 6 | ChartCanvas, 7 | XAxis, 8 | YAxis, 9 | CandlestickSeries, 10 | timeFormat, 11 | withDeviceRatio, 12 | withSize, 13 | } from "react-financial-charts"; 14 | import { IOHLCData, withOHLCData } from "../../data"; 15 | 16 | interface ChartProps { 17 | readonly data: IOHLCData[]; 18 | readonly height: number; 19 | readonly width: number; 20 | readonly ratio: number; 21 | readonly xScale?: ScaleTime; 22 | readonly yScale?: ScaleContinuousNumeric; 23 | } 24 | 25 | class Scales extends React.Component { 26 | private readonly margin = { left: 0, right: 48, top: 0, bottom: 24 }; 27 | 28 | public render() { 29 | const { data, height, ratio, width, xScale = scaleTime(), yScale } = this.props; 30 | 31 | const xAccessor = (d: IOHLCData) => d.date; 32 | const max = xAccessor(data[data.length - 1]); 33 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 34 | const xExtents = [min, max]; 35 | 36 | return ( 37 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | ); 55 | } 56 | 57 | private readonly yExtents = (data: IOHLCData) => { 58 | return [data.high, data.low]; 59 | }; 60 | } 61 | 62 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(Scales))); 63 | -------------------------------------------------------------------------------- /packages/stories/src/features/scales/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { scaleLog, scaleUtc } from "d3-scale"; 2 | import * as React from "react"; 3 | import { Daily } from "./Scales"; 4 | 5 | export default { 6 | title: "Features/Scales", 7 | }; 8 | 9 | export const continuousScale = () => ; 10 | 11 | export const utcScale = () => ; 12 | 13 | export const logScale = () => ; 14 | -------------------------------------------------------------------------------- /packages/stories/src/features/tooltips/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { HoverTooltip } from "../../../../tooltip/src/HoverTooltip"; 3 | import Tooltips from "./Tooltips"; 4 | 5 | export default { 6 | title: "Features/Tooltips", 7 | component: HoverTooltip, 8 | }; 9 | 10 | export const hover = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/features/updating/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Updating } from "./BasicLineSeries"; 3 | 4 | export default { 5 | title: "Features/Updating", 6 | }; 7 | 8 | export const continuous = () => ; 9 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/atr/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { atr } from "@react-financial-charts/indicators"; 3 | import ATRIndicator from "./AtrIndicator"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/ATR", 7 | component: atr, 8 | parameters: { 9 | componentSubtitle: "Average True Range (ATR) is an indicator that measures volatility.", 10 | }, 11 | }; 12 | 13 | export const basic = () => ; 14 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/bollingerBand/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { BollingerSeries, BollingerSeriesProps } from "../../../../series/src/BollingerSeries"; 4 | import BollingerIndicator from "./BollingerIndicator"; 5 | 6 | export default { 7 | title: "Visualization/Indicator/Bollinger Band", 8 | component: BollingerSeries, 9 | argTypes: { 10 | fillStyle: { control: "color" }, 11 | strokeStyle: { control: null }, 12 | }, 13 | }; 14 | 15 | const Template: Story = ({ fillStyle }) => ; 16 | 17 | export const basic = Template.bind({}); 18 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/compare/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import CompareIndicator from "./CompareIndicator"; 3 | 4 | export default { 5 | title: "Visualization/Indicator/Compare", 6 | }; 7 | 8 | export const basic = () => ; 9 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/elderRay/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { ElderRaySeries } from "../../../../series/src/ElderRaySeries"; 3 | import ElderRayIndicator from "./ElderRayIndicator"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/Elder Ray", 7 | component: ElderRaySeries, 8 | }; 9 | 10 | export const basic = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/ema/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import EMAIndicator from "./EmaIndicator"; 3 | 4 | export default { 5 | title: "Visualization/Indicator/EMA", 6 | parameters: { 7 | componentSubtitle: "Moving averages smooth the price data to form a trend following indicator.", 8 | }, 9 | }; 10 | 11 | export const basic = () => ; 12 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/forceIndex/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { forceIndex } from "@react-financial-charts/indicators"; 3 | import ForceIndicator from "./ForceIndicator"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/Force Index", 7 | component: forceIndex, 8 | parameters: { 9 | componentSubtitle: `The Force Index is an indicator that uses price 10 | and volume to assess the power behind a move or identify possible 11 | turning points.`, 12 | }, 13 | }; 14 | 15 | export const basic = () => ; 16 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/macd/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { MACDSeries } from "../../../../series/src/MACDSeries"; 3 | import MACDIndicator from "./MacdIndicator"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/MACD", 7 | component: MACDSeries, 8 | }; 9 | 10 | export const basic = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/rsi/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { RSISeries } from "../../../../series/src/RSISeries"; 3 | import RSIIndicator from "./RsiIndicator"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/RSI", 7 | component: RSISeries, 8 | }; 9 | 10 | export const basic = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/sar/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { SARSeries } from "../../../../series/src/SARSeries"; 3 | import SARIndicator from "./SarIndicator"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/SAR", 7 | component: SARSeries, 8 | }; 9 | 10 | export const basic = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/sto/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StochasticSeries } from "../../../../series/src/StochasticSeries"; 3 | import StoIndicator from "./StoIndicator"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/Stochastic Oscillator", 7 | component: StochasticSeries, 8 | }; 9 | 10 | export const basic = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/indicators/volumeProfile/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { VolumeProfileSeries } from "../../../../series/src/VolumeProfileSeries"; 3 | import VolumeProfile from "./VolumeProfile"; 4 | 5 | export default { 6 | title: "Visualization/Indicator/Volume Profile", 7 | component: VolumeProfileSeries, 8 | }; 9 | 10 | export const basic = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/series/area/BasicAreaSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Chart, ChartCanvas } from "@react-financial-charts/core"; 3 | import { XAxis, YAxis } from "@react-financial-charts/axes"; 4 | import { discontinuousTimeScaleProviderBuilder } from "@react-financial-charts/scales"; 5 | import { AreaSeries, AreaSeriesProps } from "@react-financial-charts/series"; 6 | import { withDeviceRatio, withSize } from "@react-financial-charts/utils"; 7 | import { IOHLCData, withOHLCData } from "../../data"; 8 | 9 | interface ChartProps extends Partial { 10 | readonly data: IOHLCData[]; 11 | readonly height: number; 12 | readonly ratio: number; 13 | readonly width: number; 14 | } 15 | 16 | class BasicAreaSeries extends React.Component { 17 | private readonly margin = { left: 0, right: 40, top: 24, bottom: 24 }; 18 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 19 | (d: IOHLCData) => d.date, 20 | ); 21 | 22 | public render() { 23 | const { data: initialData, height, ratio, width, ...rest } = this.props; 24 | 25 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(initialData); 26 | 27 | const max = xAccessor(data[data.length - 1]); 28 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 29 | const xExtents = [min, max]; 30 | 31 | return ( 32 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | } 52 | 53 | private readonly yAccessor = (data: IOHLCData) => { 54 | return data.close; 55 | }; 56 | 57 | private readonly yExtents = (data: IOHLCData) => { 58 | return [data.high, data.low]; 59 | }; 60 | } 61 | 62 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicAreaSeries))); 63 | 64 | export const Intraday = withOHLCData("MINUTES")( 65 | withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicAreaSeries)), 66 | ); 67 | -------------------------------------------------------------------------------- /packages/stories/src/series/area/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { AreaSeries, AreaSeriesProps } from "../../../../series/src/AreaSeries"; 4 | import { Daily, Intraday } from "./BasicAreaSeries"; 5 | 6 | export default { 7 | title: "Visualization/Series/Area", 8 | component: AreaSeries, 9 | argTypes: { 10 | fillStyle: { control: "color" }, 11 | strokeStyle: { control: "color" }, 12 | }, 13 | }; 14 | 15 | const Template: Story = (args) => ; 16 | 17 | export const daily = Template.bind({}); 18 | 19 | const IntradayTemplate: Story = (args) => ; 20 | 21 | export const intraday = IntradayTemplate.bind({}); 22 | -------------------------------------------------------------------------------- /packages/stories/src/series/bar/BasicBarSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Chart, ChartCanvas } from "@react-financial-charts/core"; 3 | import { XAxis, YAxis } from "@react-financial-charts/axes"; 4 | import { discontinuousTimeScaleProviderBuilder } from "@react-financial-charts/scales"; 5 | import { BarSeries, BarSeriesProps } from "@react-financial-charts/series"; 6 | import { IOHLCData, withOHLCData } from "../../data"; 7 | import { withDeviceRatio, withSize } from "@react-financial-charts/utils"; 8 | 9 | interface ChartProps extends Partial { 10 | readonly data: IOHLCData[]; 11 | readonly height: number; 12 | readonly width: number; 13 | readonly ratio: number; 14 | } 15 | 16 | class BasicBarSeries extends React.Component { 17 | private readonly margin = { left: 0, right: 90, top: 0, bottom: 24 }; 18 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 19 | (d: IOHLCData) => d.date, 20 | ); 21 | 22 | public render() { 23 | const { baseAt, data: initialData, height, ratio, width, ...rest } = this.props; 24 | 25 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(initialData); 26 | 27 | const max = xAccessor(data[data.length - 1]); 28 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 29 | const xExtents = [min, max]; 30 | 31 | return ( 32 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | } 52 | 53 | private readonly yAccessor = (data: IOHLCData) => { 54 | return data.volume; 55 | }; 56 | 57 | private readonly yExtents = (data: IOHLCData) => { 58 | return data.volume; 59 | }; 60 | } 61 | 62 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicBarSeries))); 63 | 64 | export const Intraday = withOHLCData("MINUTES")( 65 | withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicBarSeries)), 66 | ); 67 | -------------------------------------------------------------------------------- /packages/stories/src/series/bar/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { BarSeries, BarSeriesProps } from "../../../../series/src/BarSeries"; 4 | import { Daily, Intraday } from "./BasicBarSeries"; 5 | 6 | export default { 7 | component: BarSeries, 8 | title: "Visualization/Series/Bar", 9 | argTypes: { 10 | fillStyle: { control: "color" }, 11 | }, 12 | }; 13 | 14 | const Template: Story = (args) => ; 15 | 16 | export const daily = Template.bind({}); 17 | 18 | const IntradayTemplate: Story = (args) => ; 19 | 20 | export const intraday = IntradayTemplate.bind({}); 21 | -------------------------------------------------------------------------------- /packages/stories/src/series/baseline/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { 4 | AlternatingFillAreaSeries, 5 | AlternatingFillAreaSeriesProps, 6 | } from "../../../../series/src/AlternatingFillAreaSeries"; 7 | import { Daily, Intraday } from "./BasicBaselineSeries"; 8 | 9 | export default { 10 | component: AlternatingFillAreaSeries, 11 | title: "Visualization/Series/Baseline", 12 | args: { 13 | fillStyle: undefined, 14 | strokeStyle: undefined, 15 | }, 16 | argTypes: { 17 | baseAt: { control: "number" }, 18 | fillStyle: { control: "object" }, 19 | strokeStyle: { control: "object" }, 20 | }, 21 | }; 22 | 23 | const Template: Story = (args) => ; 24 | 25 | export const daily = Template.bind({}); 26 | 27 | const IntradayTemplate: Story = (args) => ; 28 | 29 | export const intraday = IntradayTemplate.bind({}); 30 | -------------------------------------------------------------------------------- /packages/stories/src/series/candlestick/BasicCandlestick.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Chart, ChartCanvas } from "@react-financial-charts/core"; 3 | import { XAxis, YAxis } from "@react-financial-charts/axes"; 4 | import { discontinuousTimeScaleProviderBuilder } from "@react-financial-charts/scales"; 5 | import { CandlestickSeries } from "@react-financial-charts/series"; 6 | import { IOHLCData, withOHLCData } from "../../data"; 7 | import { withDeviceRatio, withSize } from "@react-financial-charts/utils"; 8 | 9 | interface ChartProps { 10 | readonly data: IOHLCData[]; 11 | readonly height: number; 12 | readonly width: number; 13 | readonly ratio: number; 14 | } 15 | 16 | class BasicCandlestick extends React.Component { 17 | private readonly margin = { left: 0, right: 40, top: 0, bottom: 24 }; 18 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 19 | (d: IOHLCData) => d.date, 20 | ); 21 | 22 | public render() { 23 | const { data: initialData, height, ratio, width } = this.props; 24 | 25 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(initialData); 26 | 27 | const max = xAccessor(data[data.length - 1]); 28 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 29 | const xExtents = [min, max]; 30 | 31 | return ( 32 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | } 52 | 53 | private readonly yExtents = (data: IOHLCData) => { 54 | return [data.high, data.low]; 55 | }; 56 | } 57 | 58 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicCandlestick))); 59 | 60 | export const Intraday = withOHLCData("MINUTES")( 61 | withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicCandlestick)), 62 | ); 63 | -------------------------------------------------------------------------------- /packages/stories/src/series/candlestick/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { CandlestickSeries } from "../../../../series/src/CandlestickSeries"; 3 | import { Daily, Intraday } from "./BasicCandlestick"; 4 | 5 | export default { 6 | component: CandlestickSeries, 7 | title: "Visualization/Series/Candles", 8 | }; 9 | 10 | export const daily = () => ; 11 | 12 | export const intraday = () => ; 13 | -------------------------------------------------------------------------------- /packages/stories/src/series/heikinAshi/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { CandlestickSeries } from "../../../../series/src/CandlestickSeries"; 3 | import { Daily, Intraday } from "./BasicHeikinAshiSeries"; 4 | 5 | export default { 6 | component: CandlestickSeries, 7 | title: "Visualization/Series/Heikin Ashi", 8 | }; 9 | 10 | export const daily = () => ; 11 | 12 | export const intraday = () => ; 13 | -------------------------------------------------------------------------------- /packages/stories/src/series/kagi/BasicKagiSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Chart, ChartCanvas } from "@react-financial-charts/core"; 3 | import { XAxis, YAxis } from "@react-financial-charts/axes"; 4 | import { kagi } from "@react-financial-charts/indicators"; 5 | import { discontinuousTimeScaleProviderBuilder } from "@react-financial-charts/scales"; 6 | import { KagiSeries } from "@react-financial-charts/series"; 7 | import { IOHLCData, withOHLCData } from "../../data"; 8 | import { withDeviceRatio, withSize } from "@react-financial-charts/utils"; 9 | 10 | interface ChartProps { 11 | readonly data: IOHLCData[]; 12 | readonly height: number; 13 | readonly width: number; 14 | readonly ratio: number; 15 | } 16 | 17 | class BasicKagiSeries extends React.Component { 18 | private readonly margin = { left: 0, right: 40, top: 0, bottom: 24 }; 19 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 20 | (d: IOHLCData) => d.date, 21 | ); 22 | 23 | public render() { 24 | const { data: initialData, height, ratio, width } = this.props; 25 | 26 | const calculator = kagi(); 27 | 28 | const calculatedData = calculator(initialData); 29 | 30 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(calculatedData); 31 | 32 | const max = xAccessor(data[data.length - 1]); 33 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 34 | const xExtents = [min, max]; 35 | return ( 36 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | ); 55 | } 56 | 57 | private readonly yExtents = (data: IOHLCData) => { 58 | return [data.high, data.low]; 59 | }; 60 | } 61 | 62 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicKagiSeries))); 63 | 64 | export const Intraday = withOHLCData("MINUTES")( 65 | withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicKagiSeries)), 66 | ); 67 | -------------------------------------------------------------------------------- /packages/stories/src/series/kagi/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { KagiSeries } from "../../../../series/src/KagiSeries"; 3 | import { Daily, Intraday } from "./BasicKagiSeries"; 4 | 5 | export default { 6 | component: KagiSeries, 7 | title: "Visualization/Series/Kagi", 8 | }; 9 | 10 | export const daily = () => ; 11 | 12 | export const intraday = () => ; 13 | -------------------------------------------------------------------------------- /packages/stories/src/series/line/BasicLineSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Chart, ChartCanvas } from "@react-financial-charts/core"; 3 | import { XAxis, YAxis } from "@react-financial-charts/axes"; 4 | import { discontinuousTimeScaleProviderBuilder } from "@react-financial-charts/scales"; 5 | import { LineSeries, LineSeriesProps } from "@react-financial-charts/series"; 6 | import { IOHLCData, withOHLCData } from "../../data"; 7 | import { withDeviceRatio, withSize } from "@react-financial-charts/utils"; 8 | 9 | interface ChartProps extends Partial { 10 | readonly data: IOHLCData[]; 11 | readonly height: number; 12 | readonly width: number; 13 | readonly ratio: number; 14 | } 15 | 16 | class BasicLineSeries extends React.Component { 17 | private readonly margin = { left: 0, right: 40, top: 0, bottom: 24 }; 18 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 19 | (d: IOHLCData) => d.date, 20 | ); 21 | 22 | public render() { 23 | const { data: initialData, defined, height, ratio, width, ...rest } = this.props; 24 | 25 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(initialData); 26 | 27 | const max = xAccessor(data[data.length - 1]); 28 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 29 | const xExtents = [min, max]; 30 | 31 | return ( 32 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | } 52 | 53 | private readonly yAccessor = (data: IOHLCData) => { 54 | return data.close; 55 | }; 56 | 57 | private readonly yExtents = (data: IOHLCData) => { 58 | return [data.high, data.low]; 59 | }; 60 | } 61 | 62 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicLineSeries))); 63 | 64 | export const Intraday = withOHLCData("MINUTES")( 65 | withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicLineSeries)), 66 | ); 67 | -------------------------------------------------------------------------------- /packages/stories/src/series/line/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story } from "@storybook/react"; 2 | import * as React from "react"; 3 | import { LineSeries, LineSeriesProps } from "../../../../series/src/LineSeries"; 4 | import { Daily, Intraday } from "./BasicLineSeries"; 5 | 6 | export default { 7 | component: LineSeries, 8 | title: "Visualization/Series/Line", 9 | argTypes: { 10 | strokeStyle: { control: "color" }, 11 | }, 12 | }; 13 | 14 | const Template: Story = (args) => ; 15 | 16 | export const daily = Template.bind({}); 17 | 18 | const IntradayTemplate: Story = (args) => ; 19 | 20 | export const intraday = IntradayTemplate.bind({}); 21 | -------------------------------------------------------------------------------- /packages/stories/src/series/ohlc/BasicOHLCSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Chart, ChartCanvas } from "@react-financial-charts/core"; 3 | import { XAxis, YAxis } from "@react-financial-charts/axes"; 4 | import { change } from "@react-financial-charts/indicators"; 5 | import { discontinuousTimeScaleProviderBuilder } from "@react-financial-charts/scales"; 6 | import { OHLCSeries } from "@react-financial-charts/series"; 7 | import { IOHLCData, withOHLCData } from "../../data"; 8 | import { withDeviceRatio, withSize } from "@react-financial-charts/utils"; 9 | 10 | interface ChartProps { 11 | readonly data: IOHLCData[]; 12 | readonly height: number; 13 | readonly width: number; 14 | readonly ratio: number; 15 | } 16 | 17 | class BasicOHLCSeries extends React.Component { 18 | private readonly margin = { left: 0, right: 40, top: 0, bottom: 24 }; 19 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 20 | (d: IOHLCData) => d.date, 21 | ); 22 | 23 | public render() { 24 | const { data: initialData, height, ratio, width } = this.props; 25 | 26 | const calculator = change(); 27 | 28 | const calculatedData = calculator(initialData); 29 | 30 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(calculatedData); 31 | 32 | const max = xAccessor(data[data.length - 1]); 33 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 34 | const xExtents = [min, max]; 35 | 36 | return ( 37 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ); 56 | } 57 | 58 | private readonly yExtents = (data: IOHLCData) => { 59 | return [data.high, data.low]; 60 | }; 61 | } 62 | 63 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicOHLCSeries))); 64 | 65 | export const Intraday = withOHLCData("MINUTES")( 66 | withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicOHLCSeries)), 67 | ); 68 | -------------------------------------------------------------------------------- /packages/stories/src/series/ohlc/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { OHLCSeries } from "../../../../series/src/OHLCSeries"; 3 | import { Daily, Intraday } from "./BasicOHLCSeries"; 4 | 5 | export default { 6 | component: OHLCSeries, 7 | title: "Visualization/Series/OHLC", 8 | }; 9 | 10 | export const daily = () => ; 11 | 12 | export const intraday = () => ; 13 | -------------------------------------------------------------------------------- /packages/stories/src/series/pointAndFigure/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { PointAndFigureSeries } from "../../../../series/src/PointAndFigureSeries"; 3 | import { Daily } from "./BasicPointAndFigureSeries"; 4 | 5 | export default { 6 | component: PointAndFigureSeries, 7 | title: "Visualization/Series/Point & Figure", 8 | }; 9 | 10 | export const daily = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/src/series/renko/BasicRenkoSeries.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Chart, ChartCanvas } from "@react-financial-charts/core"; 3 | import { XAxis, YAxis } from "@react-financial-charts/axes"; 4 | import { renko } from "@react-financial-charts/indicators"; 5 | import { discontinuousTimeScaleProviderBuilder } from "@react-financial-charts/scales"; 6 | import { RenkoSeries } from "@react-financial-charts/series"; 7 | import { IOHLCData, withOHLCData } from "../../data"; 8 | import { withDeviceRatio, withSize } from "@react-financial-charts/utils"; 9 | 10 | interface ChartProps { 11 | readonly data: IOHLCData[]; 12 | readonly height: number; 13 | readonly ratio: number; 14 | readonly width: number; 15 | } 16 | 17 | class BasicRenkoSeries extends React.Component { 18 | private readonly margin = { left: 0, right: 40, top: 0, bottom: 24 }; 19 | private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( 20 | (d: IOHLCData) => d.date, 21 | ); 22 | 23 | public render() { 24 | const { data: initialData, height, ratio, width } = this.props; 25 | 26 | const calculator = renko(); 27 | 28 | const calculatedData = calculator(initialData); 29 | 30 | const { data, xScale, xAccessor, displayXAccessor } = this.xScaleProvider(calculatedData); 31 | 32 | const max = xAccessor(data[data.length - 1]); 33 | const min = xAccessor(data[Math.max(0, data.length - 100)]); 34 | const xExtents = [min, max]; 35 | 36 | return ( 37 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ); 56 | } 57 | 58 | private readonly yExtents = (data: IOHLCData) => { 59 | return [data.high, data.low]; 60 | }; 61 | } 62 | 63 | export const Daily = withOHLCData()(withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicRenkoSeries))); 64 | 65 | export const Intraday = withOHLCData("MINUTES")( 66 | withSize({ style: { minHeight: 600 } })(withDeviceRatio()(BasicRenkoSeries)), 67 | ); 68 | -------------------------------------------------------------------------------- /packages/stories/src/series/renko/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { RenkoSeries } from "../../../../series/src/RenkoSeries"; 3 | import { Daily, Intraday } from "./BasicRenkoSeries"; 4 | 5 | export default { 6 | component: RenkoSeries, 7 | title: "Visualization/Series/Renko", 8 | }; 9 | 10 | export const daily = () => ; 11 | 12 | export const intraday = () => ; 13 | -------------------------------------------------------------------------------- /packages/stories/src/series/scatter/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { ScatterSeries } from "../../../../series/src/ScatterSeries"; 3 | import BasicScatterSeries from "./BasicScatterSeries"; 4 | 5 | export default { 6 | component: ScatterSeries, 7 | title: "Visualization/Series/Scatter", 8 | }; 9 | 10 | export const bubble = () => ; 11 | -------------------------------------------------------------------------------- /packages/stories/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "emitDecoratorMetadata": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "importHelpers": true, 8 | "jsx": "react", 9 | "lib": [ 10 | "es6", 11 | "esnext", 12 | "dom", 13 | ], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitAny": true, 18 | "noImplicitThis": true, 19 | "noResolve": false, 20 | "outDir": "lib", 21 | "sourceMap": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "target": "es6" 25 | }, 26 | "include": [ 27 | "src", 28 | "../series/src" 29 | ], 30 | "exclude": [ 31 | "node_modules", 32 | "lib" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/tooltip/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/tooltip/README.md: -------------------------------------------------------------------------------- 1 | # Tooltip 2 | 3 | ```bash 4 | npm i @react-financial-charts/tooltip 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/tooltip/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/tooltip", 3 | "version": "2.0.0", 4 | "description": "Tooltips for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "@react-financial-charts/core": "file:../core", 42 | "d3-array": "^2.9.1", 43 | "d3-format": "^2.0.0", 44 | "d3-time-format": "^3.0.0" 45 | }, 46 | "peerDependencies": { 47 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 48 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/tooltip/src/ToolTipTSpanLabel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export class ToolTipTSpanLabel extends React.PureComponent> { 4 | public static defaultProps = { 5 | className: "react-financial-charts-tooltip-label", 6 | fill: "#4682B4", 7 | }; 8 | 9 | public render() { 10 | const { children, ...rest } = this.props; 11 | 12 | return {children}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/tooltip/src/ToolTipText.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export class ToolTipText extends React.PureComponent> { 4 | public static defaultProps = { 5 | className: "react-financial-charts-tooltip", 6 | fontFamily: "-apple-system, system-ui, 'Helvetica Neue', Ubuntu, sans-serif", 7 | fontSize: 11, 8 | }; 9 | 10 | public render() { 11 | const { children, ...rest } = this.props; 12 | 13 | return {children}; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/tooltip/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./BollingerBandTooltip"; 2 | export * from "./GroupTooltip"; 3 | export * from "./HoverTooltip"; 4 | export * from "./MACDTooltip"; 5 | export * from "./MovingAverageTooltip"; 6 | export * from "./OHLCTooltip"; 7 | export * from "./RSITooltip"; 8 | export * from "./SingleTooltip"; 9 | export * from "./SingleValueTooltip"; 10 | export * from "./StochasticTooltip"; 11 | export * from "./ToolTipText"; 12 | export * from "./ToolTipTSpanLabel"; 13 | -------------------------------------------------------------------------------- /packages/tooltip/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/utils/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | https://github.com/reactivemarkets/react-financial-charts 3 | 4 | Copyright (c) 2015-2018 Ragu Ramaswamy 5 | Copyright (c) 2016 Julien Renaux 6 | Copyright (c) 2019 Reactive Markets 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | # Utils 2 | 3 | ```bash 4 | npm i @react-financial-charts/utils 5 | ``` 6 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-financial-charts/utils", 3 | "version": "2.0.1", 4 | "description": "Utils for react-financial-charts", 5 | "publishConfig": { 6 | "access": "public" 7 | }, 8 | "main": "./lib/index.js", 9 | "typings": "./lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "src" 13 | ], 14 | "sideEffects": false, 15 | "author": "Reactive Markets", 16 | "keywords": [ 17 | "charts", 18 | "charting", 19 | "stockcharts", 20 | "finance", 21 | "financial", 22 | "finance-chart", 23 | "react", 24 | "d3" 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/reactivemarkets/react-financial-charts.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/reactivemarkets/react-financial-charts/issues" 33 | }, 34 | "scripts": { 35 | "build": "npm run clean && npm run compile", 36 | "clean": "rimraf lib", 37 | "compile": "tsc -p tsconfig.json", 38 | "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" 39 | }, 40 | "dependencies": { 41 | "react-virtualized-auto-sizer": "^1.0.16" 42 | }, 43 | "peerDependencies": { 44 | "react": "^16.0.0 || ^17.0.0 || ^18.0.0", 45 | "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./withDeviceRatio"; 2 | export * from "./withSize"; 3 | -------------------------------------------------------------------------------- /packages/utils/src/withDeviceRatio.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export interface WithRatioProps { 4 | readonly ratio: number; 5 | } 6 | 7 | export interface WithRatioState { 8 | ratio: number; 9 | } 10 | 11 | export const withDeviceRatio = () => { 12 | return (OriginalComponent: React.ComponentClass) => { 13 | return class WithRatio extends React.Component, WithRatioState> { 14 | public readonly ref = React.createRef(); 15 | 16 | public componentDidMount() { 17 | const { current } = this.ref; 18 | if (current === null) { 19 | this.setState({ 20 | ratio: 1, 21 | }); 22 | 23 | return; 24 | } 25 | 26 | const context: any = current.getContext("2d"); 27 | 28 | const { devicePixelRatio } = window; 29 | 30 | const backingStoreRatio = 31 | context.webkitBackingStorePixelRatio ?? 32 | context.mozBackingStorePixelRatio ?? 33 | context.msBackingStorePixelRatio ?? 34 | context.oBackingStorePixelRatio ?? 35 | context.backingStorePixelRatio ?? 36 | 1; 37 | 38 | this.setState({ 39 | ratio: devicePixelRatio / backingStoreRatio, 40 | }); 41 | } 42 | 43 | public render() { 44 | const state = this.state; 45 | if (state !== null) { 46 | return ; 47 | } 48 | 49 | return ; 50 | } 51 | }; 52 | }; 53 | }; 54 | -------------------------------------------------------------------------------- /packages/utils/src/withSize.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import AutoSizer, { Props as AutoSizerProps } from "react-virtualized-auto-sizer"; 3 | 4 | export interface WithSizeProps { 5 | readonly width: number; 6 | readonly height: number; 7 | } 8 | 9 | export const withSize = (props?: Omit) => { 10 | return (OriginalComponent: React.ComponentClass) => { 11 | return class WithSize extends React.Component> { 12 | public render() { 13 | return ( 14 | 15 | {({ height, width }) => { 16 | return ; 17 | }} 18 | 19 | ); 20 | } 21 | }; 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "outDir": "lib", 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "include": [ 19 | "src" 20 | ], 21 | "exclude": [ 22 | "lib", 23 | "**/__tests__/**" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------