├── .expo-shared
└── assets.json
├── .gitignore
├── .watchmanconfig
├── App.js
├── Index.js
├── README.md
├── __tests__
└── App-test.js
├── app.json
├── assets
├── fonts
│ └── SpaceMono-Regular.ttf
└── images
│ ├── Crypto.jpg
│ ├── favicon.png
│ ├── icon.png
│ ├── iconCapital.png
│ ├── iron-man.png
│ ├── marketOverall.jpg
│ ├── robot-dev.png
│ ├── robot-prod.png
│ ├── sector.jpg
│ ├── splash.png
│ └── stockScreen.jpg
├── babel.config.js
├── components
├── Crypto
│ └── TreeMap.js
├── Home
│ └── StockChart.js
├── Sector
│ └── SectorChart.js
├── Stock
│ ├── Chart.js
│ ├── InfoSlideOne.js
│ ├── InfoSlideThree.js
│ ├── InfoSlideTwo.js
│ └── Input.js
├── StyledText.js
├── TabBarIcon.js
├── __tests__
│ ├── StyledText-test.js
│ └── __snapshots__
│ │ └── StyledText-test.js.snap
├── sector
│ └── SectorChart.js
└── stock
│ ├── Chart.js
│ └── Input.js
├── constants
├── Colors.js
└── Layout.js
├── hooks
└── useCachedResources.js
├── navigation
├── BottomTabNavigator.js
└── LinkingConfiguration.js
├── package-lock.json
├── package.json
└── screens
├── MarketOverview.js
├── SectorScreen.js
├── StockScreen.js
└── utils.js
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true,
3 | "af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true,
4 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
5 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 |
12 | # macOS
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import { NavigationContainer } from '@react-navigation/native';
2 | import { createStackNavigator } from '@react-navigation/stack';
3 | import * as React from 'react';
4 | import { Platform, StatusBar, StyleSheet, View } from 'react-native';
5 |
6 | import useCachedResources from './hooks/useCachedResources';
7 | import BottomTabNavigator from './navigation/BottomTabNavigator';
8 | import LinkingConfiguration from './navigation/LinkingConfiguration';
9 |
10 | const Stack = createStackNavigator();
11 |
12 | export default function App(props) {
13 | const isLoadingComplete = useCachedResources();
14 |
15 | if (!isLoadingComplete) {
16 | return null;
17 | } else {
18 | return (
19 |
20 | {Platform.OS === 'ios' && }
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 | }
29 | }
30 |
31 | const styles = StyleSheet.create({
32 | container: {
33 | flex: 1,
34 | backgroundColor: '#fff',
35 | },
36 | });
37 |
--------------------------------------------------------------------------------
/Index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Provider } from 'react-redux';
3 | import store from './stores/store';
4 | import App from './App';
5 |
6 | export default class Index extends Component {
7 | render() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Financial InfoX
2 |
3 | `Tech Used:` React-Native, native-echarts, expo, antd-mobile,react-navigation
4 |
5 | ## Description:
6 |
7 | A mobile app built in 3 days to demonstrate stock market information by using a variety of charts, especially candlestick charts. In addition, used a treemap (whcih now is not working, need API resource) to illustrate asset value for Cryptocurrency market.
8 |
9 | ## Updated:
10 | Updated In May 2020 to adapt to all new packages and make the project alive again!
11 |
12 | ## Screen Shots demonstration
13 |
14 |
15 |
16 |
17 |
18 |
19 | #### Question?
20 |
21 | Now, if you have question, please let me know.
22 |
--------------------------------------------------------------------------------
/__tests__/App-test.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import App from '../App';
4 | import renderer from 'react-test-renderer';
5 | import NavigationTestUtils from 'react-navigation/NavigationTestUtils';
6 |
7 | describe('App snapshot', () => {
8 | jest.useFakeTimers();
9 | beforeEach(() => {
10 | NavigationTestUtils.resetInternalState();
11 | });
12 |
13 | it('renders the loading screen', async () => {
14 | const tree = renderer.create().toJSON();
15 | expect(tree).toMatchSnapshot();
16 | });
17 |
18 | it('renders the root without loading screen', async () => {
19 | const tree = renderer.create().toJSON();
20 | expect(tree).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "StockProject",
4 | "slug": "StockProject",
5 | "platforms": [
6 | "ios",
7 | "android",
8 | "web"
9 | ],
10 | "version": "1.0.0",
11 | "orientation": "portrait",
12 | "icon": "./assets/images/icon.png",
13 | "scheme": "myapp",
14 | "splash": {
15 | "image": "./assets/images/splash.png",
16 | "resizeMode": "contain",
17 | "backgroundColor": "#ffffff"
18 | },
19 | "updates": {
20 | "fallbackToCacheTimeout": 0
21 | },
22 | "assetBundlePatterns": [
23 | "**/*"
24 | ],
25 | "ios": {
26 | "supportsTablet": true
27 | },
28 | "web": {
29 | "favicon": "./assets/images/favicon.png"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/assets/fonts/SpaceMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/fonts/SpaceMono-Regular.ttf
--------------------------------------------------------------------------------
/assets/images/Crypto.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/Crypto.jpg
--------------------------------------------------------------------------------
/assets/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/favicon.png
--------------------------------------------------------------------------------
/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/icon.png
--------------------------------------------------------------------------------
/assets/images/iconCapital.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/iconCapital.png
--------------------------------------------------------------------------------
/assets/images/iron-man.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/iron-man.png
--------------------------------------------------------------------------------
/assets/images/marketOverall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/marketOverall.jpg
--------------------------------------------------------------------------------
/assets/images/robot-dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/robot-dev.png
--------------------------------------------------------------------------------
/assets/images/robot-prod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/robot-prod.png
--------------------------------------------------------------------------------
/assets/images/sector.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/sector.jpg
--------------------------------------------------------------------------------
/assets/images/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/splash.png
--------------------------------------------------------------------------------
/assets/images/stockScreen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/biill/ReactNativeStockProject/0ab5a4a1b2fd770851df2f1d4f9dd38413eccde6/assets/images/stockScreen.jpg
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/components/Crypto/TreeMap.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { StyleSheet, View, Platform } from "react-native";
3 | import Echarts from "native-echarts";
4 | import { useEffect, useState } from "react";
5 | import axios from "axios";
6 | const CryptoTreeMap = () => {
7 | const [cryptoData, setCryptoData] = useState(null);
8 | const fetchSector = async () => {
9 | try {
10 | const sectorRes = await axios.get(
11 | `https://www.alphavantage.co/query?function=SECTOR&apikey=demo`
12 | );
13 | setSectorData(sectorRes.data[`Rank B: 1 Day Performance`]);
14 | } catch (error) {
15 | console.log(error);
16 | }
17 | };
18 |
19 | useEffect(() => {
20 | fetchSector();
21 | }, []);
22 | const name = "Market Change by (%)";
23 | const sectors = Object.keys(sectorData);
24 | const performance = Object.values(sectorData).map((e) =>
25 | parseFloat(e).toFixed(2)
26 | );
27 |
28 | const option = {
29 | tooltip: {
30 | trigger: "axis",
31 | axisPointer: {
32 | type: "shadow",
33 | },
34 | },
35 | legend: {
36 | data: [name],
37 | textStyle: {
38 | color: "white",
39 | },
40 | },
41 | grid: {
42 | left: "1%",
43 | right: "4%",
44 | bottom: "3%",
45 | containLabel: true,
46 | },
47 | xAxis: [
48 | {
49 | type: "value",
50 | axisLine: {
51 | lineStyle: { color: "white" },
52 | },
53 | axisTick: { show: false },
54 | },
55 | ],
56 | yAxis: [
57 | {
58 | type: "category",
59 | axisTick: { show: false },
60 | axisLine: {
61 | lineStyle: { color: "white" },
62 | },
63 | splitLine: { show: false, color: "black" },
64 | data: sectors,
65 | },
66 | ],
67 | series: [
68 | {
69 | name: name,
70 | type: "bar",
71 | label: {
72 | normal: {
73 | show: true,
74 | position: "inside",
75 | },
76 | },
77 | data: performance,
78 | },
79 | ],
80 | };
81 | return (
82 |
83 |
84 |
85 | );
86 | };
87 |
88 | const styles = StyleSheet.create({
89 | container: {
90 | flex: 1,
91 | paddingLeft: 10,
92 | paddingRight: 10,
93 | backgroundColor: "black",
94 | },
95 | nameBlock: {
96 | flex: 1,
97 | paddingTop: 4,
98 | justifyContent: "center",
99 | alignItems: "center",
100 | },
101 | nameText: {
102 | fontWeight: "bold",
103 | fontSize: 12,
104 | color: "white",
105 | },
106 | details: {
107 | flex: 5,
108 | flexDirection: "column",
109 | borderTopWidth: StyleSheet.hairlineWidth,
110 | borderBottomWidth: StyleSheet.hairlineWidth,
111 | borderColor: "white",
112 | },
113 | detailsRow: {
114 | flex: 1,
115 | flexDirection: "row",
116 | alignItems: "center",
117 | justifyContent: "space-between",
118 | marginTop: 10,
119 | },
120 | detailsRowColumn: {
121 | flex: 1,
122 | flexDirection: "row",
123 | alignItems: "center",
124 | justifyContent: "space-between",
125 | paddingLeft: 5,
126 | paddingRight: 5,
127 | },
128 | separator: {
129 | height: StyleSheet.hairlineWidth,
130 | backgroundColor: "white",
131 | marginTop: 20,
132 | },
133 | separatorThin: {
134 | height: StyleSheet.hairlineWidth,
135 | backgroundColor: "#A6A6A6",
136 | },
137 | propertyText: {
138 | fontSize: 12,
139 | color: "#A6A6A6",
140 | textAlign: "left",
141 | },
142 | valueText: {
143 | fontSize: 15,
144 | color: "white",
145 | textAlign: "right",
146 | },
147 | title: {
148 | paddingTop: 5,
149 | color: "white",
150 | fontSize: 15,
151 | fontWeight: "bold",
152 | textAlign: "center",
153 | justifyContent: "center",
154 | alignItems: "center",
155 | },
156 | });
157 |
158 | export default SectorChart;
159 |
--------------------------------------------------------------------------------
/components/Home/StockChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { StyleSheet, View, Platform } from "react-native";
3 | import Echarts from "native-echarts";
4 |
5 | const StockChart = ({ data, name }) => {
6 | let stockData = data.slice(-30);
7 |
8 | let dates = [];
9 | let values = [];
10 |
11 | if (data) {
12 | stockData.map((data) => dates.push(data["date"]));
13 | stockData.map((item) =>
14 | values.push([item["open"], item["close"], item["low"], item["high"]])
15 | );
16 | }
17 |
18 | const option = {
19 | backgroundColor: "black",
20 | title: {
21 | show: true,
22 | text: name,
23 | textStyle: {
24 | fontSize: 15,
25 | align: "center",
26 | lineHeight: 40,
27 | left: "40%",
28 | },
29 | },
30 | tooltip: {
31 | trigger: "none",
32 | axisPointer: {
33 | animation: false,
34 | type: "cross",
35 | lineStyle: {
36 | color: "#376df4",
37 | width: 2,
38 | opacity: 1,
39 | },
40 | },
41 | },
42 |
43 | toolbox: {
44 | orient: "vertical",
45 | show: true,
46 | showTitle: true,
47 | },
48 | xAxis: [
49 | {
50 | type: "category",
51 | data: dates,
52 | axisLine: { lineStyle: { color: "white" } },
53 | scale: true,
54 | },
55 | ],
56 | yAxis: [
57 | {
58 | scale: true,
59 | axisLine: {
60 | lineStyle: { color: "white" },
61 | },
62 | splitLine: { show: false },
63 | },
64 | ],
65 | grid: [
66 | {
67 | top: 40,
68 | bottom: 40,
69 | left: 50,
70 | },
71 | ],
72 | color: ["rgb(249,159,94)", "rgb(67,205,126)"],
73 | animation: true,
74 | series: [
75 | {
76 | type: "candlestick",
77 | name: "Daily",
78 | data: values,
79 | itemStyle: {
80 | normal: {
81 | color: "#FD1050",
82 | color0: "#0CF49B",
83 | borderColor: "#FD1050",
84 | borderColor0: "#0CF49B",
85 | },
86 | },
87 | },
88 | ],
89 | };
90 | return (
91 |
92 |
98 |
99 | );
100 | };
101 |
102 | const styles = StyleSheet.create({
103 | container: {
104 | flex: 1,
105 | },
106 |
107 | titleView: {
108 | height: Platform.OS == "ios" ? 20 : 44,
109 | paddingTop: Platform.OS == "ios" ? 0 : 0,
110 | backgroundColor: "black",
111 | justifyContent: "center",
112 | alignItems: "center",
113 | },
114 | title: {
115 | color: "white",
116 | fontSize: 15,
117 | fontWeight: "bold",
118 | textAlign: "center",
119 | },
120 | });
121 |
122 | export default StockChart;
123 |
--------------------------------------------------------------------------------
/components/Sector/SectorChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { StyleSheet, View, Platform } from "react-native";
3 | import Echarts from "native-echarts";
4 | import { useEffect, useState } from "react";
5 | import axios from "axios";
6 | const SectorChart = () => {
7 | const [sectorData, setSectorData] = useState([]);
8 | const fetchSector = async () => {
9 | try {
10 | const sectorRes = await axios.get(
11 | `https://www.alphavantage.co/query?function=SECTOR&apikey=demo`
12 | );
13 | setSectorData(sectorRes.data[`Rank B: 1 Day Performance`]);
14 | } catch (error) {
15 | console.log(error);
16 | }
17 | };
18 |
19 | useEffect(() => {
20 | fetchSector();
21 | }, []);
22 | const name = "Market Change by (%)";
23 | const sectors = Object.keys(sectorData);
24 | const performance = Object.values(sectorData).map((e) =>
25 | parseFloat(e).toFixed(2)
26 | );
27 |
28 | const option = {
29 | tooltip: {
30 | trigger: "axis",
31 | axisPointer: {
32 | type: "shadow",
33 | },
34 | },
35 | legend: {
36 | data: [name],
37 | textStyle: {
38 | color: "white",
39 | },
40 | },
41 | grid: {
42 | left: "1%",
43 | right: "4%",
44 | bottom: "3%",
45 | containLabel: true,
46 | },
47 | xAxis: [
48 | {
49 | type: "value",
50 | axisLine: {
51 | lineStyle: { color: "white" },
52 | },
53 | axisTick: { show: false },
54 | },
55 | ],
56 | yAxis: [
57 | {
58 | type: "category",
59 | axisTick: { show: false },
60 | axisLine: {
61 | lineStyle: { color: "white" },
62 | },
63 | splitLine: { show: false, color: "black" },
64 | data: sectors,
65 | },
66 | ],
67 | series: [
68 | {
69 | name: name,
70 | type: "bar",
71 | label: {
72 | normal: {
73 | show: true,
74 | position: "inside",
75 | },
76 | },
77 | data: performance,
78 | },
79 | ],
80 | };
81 | return (
82 |
83 |
84 |
85 | );
86 | };
87 |
88 | const styles = StyleSheet.create({
89 | container: {
90 | flex: 1,
91 | paddingLeft: 10,
92 | paddingRight: 10,
93 | backgroundColor: "black",
94 | },
95 | nameBlock: {
96 | flex: 1,
97 | paddingTop: 4,
98 | justifyContent: "center",
99 | alignItems: "center",
100 | },
101 | nameText: {
102 | fontWeight: "bold",
103 | fontSize: 12,
104 | color: "white",
105 | },
106 | details: {
107 | flex: 5,
108 | flexDirection: "column",
109 | borderTopWidth: StyleSheet.hairlineWidth,
110 | borderBottomWidth: StyleSheet.hairlineWidth,
111 | borderColor: "white",
112 | },
113 | detailsRow: {
114 | flex: 1,
115 | flexDirection: "row",
116 | alignItems: "center",
117 | justifyContent: "space-between",
118 | marginTop: 10,
119 | },
120 | detailsRowColumn: {
121 | flex: 1,
122 | flexDirection: "row",
123 | alignItems: "center",
124 | justifyContent: "space-between",
125 | paddingLeft: 5,
126 | paddingRight: 5,
127 | },
128 | separator: {
129 | height: StyleSheet.hairlineWidth,
130 | backgroundColor: "white",
131 | marginTop: 20,
132 | },
133 | separatorThin: {
134 | height: StyleSheet.hairlineWidth,
135 | backgroundColor: "#A6A6A6",
136 | },
137 | propertyText: {
138 | fontSize: 12,
139 | color: "#A6A6A6",
140 | textAlign: "left",
141 | },
142 | valueText: {
143 | fontSize: 15,
144 | color: "white",
145 | textAlign: "right",
146 | },
147 | title: {
148 | paddingTop: 5,
149 | color: "white",
150 | fontSize: 15,
151 | fontWeight: "bold",
152 | textAlign: "center",
153 | justifyContent: "center",
154 | alignItems: "center",
155 | },
156 | });
157 |
158 | export default SectorChart;
159 |
--------------------------------------------------------------------------------
/components/Stock/Chart.js:
--------------------------------------------------------------------------------
1 | import Echarts from "native-echarts";
2 | import React, { Component } from "react";
3 | import { StyleSheet, Text, View, Platform, Dimensions } from "react-native";
4 |
5 | const { width } = Dimensions.get("window");
6 |
7 | export default function Chart({ selectedStock }) {
8 | const { data, info } = selectedStock;
9 | const calculateMA = (dayCount, data) => {
10 | const result = [];
11 | for (let i = 0, len = data.length; i < len; i++) {
12 | if (i < dayCount) {
13 | result.push("-");
14 | continue;
15 | }
16 | var sum = 0;
17 |
18 | for (let j = 0; j < dayCount; j++) {
19 | sum += data[i - j][1];
20 | }
21 | result.push(sum / dayCount);
22 | }
23 | return result;
24 | };
25 |
26 | let dates = [];
27 | let values = [];
28 |
29 | if (data) {
30 | data.map((stockData) => dates.push(stockData["date"]));
31 | data.map((item) =>
32 | values.push([item["open"], item["close"], item["low"], item["high"]])
33 | );
34 | }
35 | const option = {
36 | backgroundColor: "black",
37 | tooltip: {
38 | trigger: "none",
39 | axisPointer: {
40 | animation: false,
41 | type: "cross",
42 | lineStyle: {
43 | color: "#376df4",
44 | width: 2,
45 | opacity: 1,
46 | },
47 | },
48 | },
49 | legend: {
50 | data: ["MA5", "MA10", "MA20", "MA30"],
51 | inactiveColor: "#777",
52 | textStyle: {
53 | color: "#fff",
54 | },
55 | },
56 | toolbox: {
57 | orient: "vertical",
58 | show: true,
59 | showTitle: true,
60 | },
61 | xAxis: [
62 | {
63 | type: "category",
64 | data: dates,
65 | axisLine: { lineStyle: { color: "#F3F3F3" } },
66 | },
67 | ],
68 | yAxis: [
69 | {
70 | scale: true,
71 | axisLine: {
72 | lineStyle: { color: "#F3F3F3" },
73 | },
74 | splitLine: { show: false },
75 | },
76 | ],
77 | grid: [
78 | {
79 | left: "10%",
80 | right: "8%",
81 | height: "50%",
82 | },
83 | {
84 | left: "10%",
85 | right: "8%",
86 | bottom: "10%",
87 | height: "15%",
88 | },
89 | ],
90 |
91 | dataZoom: [
92 | {
93 | textStyle: {
94 | color: "#F3F3F3",
95 | },
96 | handleIcon:
97 | "M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z",
98 | handleSize: "80%",
99 | dataBackground: {
100 | areaStyle: {
101 | color: "#8392A5",
102 | },
103 | lineStyle: {
104 | opacity: 0.8,
105 | color: "#8392A5",
106 | },
107 | },
108 | handleStyle: {
109 | color: "#fff",
110 | shadowBlur: 3,
111 | shadowColor: "rgba(0, 0, 0, 0.6)",
112 | shadowOffsetX: 2,
113 | shadowOffsetY: 2,
114 | },
115 | },
116 | {
117 | type: "inside",
118 | },
119 | ],
120 | color: ["rgb(249,159,94)", "rgb(67,205,126)"],
121 | animation: true,
122 | series: [
123 | {
124 | type: "candlestick",
125 | name: "Daily",
126 | data: values,
127 | itemStyle: {
128 | normal: {
129 | color: "#FD1050",
130 | color0: "#0CF49B",
131 | borderColor: "#FD1050",
132 | borderColor0: "#0CF49B",
133 | },
134 | },
135 | },
136 | {
137 | name: "MA5",
138 | type: "line",
139 | data: calculateMA(5, values),
140 | smooth: true,
141 | showSymbol: false,
142 | lineStyle: {
143 | normal: {
144 | width: 1,
145 | },
146 | },
147 | },
148 | {
149 | name: "MA10",
150 | type: "line",
151 | data: calculateMA(10, values),
152 | smooth: true,
153 | showSymbol: false,
154 | lineStyle: {
155 | normal: {
156 | width: 1,
157 | },
158 | },
159 | },
160 | {
161 | name: "MA20",
162 | type: "line",
163 | data: calculateMA(20, values),
164 | smooth: true,
165 | showSymbol: false,
166 | lineStyle: {
167 | normal: {
168 | width: 1,
169 | },
170 | },
171 | },
172 | {
173 | name: "MA30",
174 | type: "line",
175 | data: calculateMA(30, values),
176 | smooth: true,
177 | showSymbol: false,
178 | lineStyle: {
179 | normal: {
180 | width: 1,
181 | },
182 | },
183 | },
184 | ],
185 | };
186 |
187 | return (
188 |
189 |
190 | {info && {info["companyName"]}}
191 |
192 |
193 |
194 |
195 | );
196 | }
197 | const styles = StyleSheet.create({
198 | container: {
199 | flex: 1,
200 | },
201 |
202 | titleView: {
203 | height: Platform.OS == "ios" ? 20 : 44,
204 | paddingTop: Platform.OS == "ios" ? 0 : 0,
205 | backgroundColor: "black",
206 | justifyContent: "center",
207 | alignItems: "center",
208 | },
209 | title: {
210 | color: "white",
211 | fontSize: 15,
212 | fontWeight: "bold",
213 | textAlign: "center",
214 | },
215 | });
216 |
--------------------------------------------------------------------------------
/components/Stock/InfoSlideOne.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { StyleSheet, Text, View } from "react-native";
3 |
4 | export default function InfoSlideOne({ info }) {
5 | return (
6 |
7 |
8 |
9 |
10 | 50 DAYS MA
11 |
12 | {parseFloat(info["day50MovingAvg"]).toFixed(2) || "--"}
13 |
14 |
15 |
16 | 200 DAYS MA
17 |
18 | {parseFloat(info["day200MovingAvg"]).toFixed(2) || "--"}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 5 DAYS CHANGE(%)
27 |
28 | {parseFloat(info["day5ChangePercent"]).toFixed(2) || "--"}
29 |
30 |
31 |
32 | 30 DAYS CHANGE(%)
33 |
34 | {parseFloat(info["day30ChangePercent"]).toFixed(2) || "--"}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 6 MONTH CHANGE(%)
43 |
44 | {parseFloat(info["month6ChangePercent"]).toFixed(2) || "--"}
45 |
46 |
47 |
48 | 1 YEAR CHANGE(%)
49 |
50 | {parseFloat(info["year1ChangePercent"]).toFixed(2) || "--"}
51 |
52 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 | const styles = StyleSheet.create({
60 | container: {
61 | flex: 1,
62 | paddingLeft: 10,
63 | paddingRight: 10,
64 | backgroundColor: "black",
65 | },
66 | nameBlock: {
67 | flex: 1,
68 | paddingTop: 4,
69 | justifyContent: "center",
70 | alignItems: "center",
71 | },
72 | nameText: {
73 | fontWeight: "bold",
74 | fontSize: 12,
75 | color: "white",
76 | },
77 | details: {
78 | flex: 5,
79 | flexDirection: "column",
80 | borderTopWidth: StyleSheet.hairlineWidth,
81 | borderBottomWidth: StyleSheet.hairlineWidth,
82 | borderColor: "white",
83 | },
84 | detailsRow: {
85 | flex: 1,
86 | flexDirection: "row",
87 | alignItems: "center",
88 | justifyContent: "space-between",
89 | },
90 | detailsRowColumn: {
91 | flex: 1,
92 | flexDirection: "row",
93 | alignItems: "center",
94 | justifyContent: "space-between",
95 | paddingLeft: 5,
96 | paddingRight: 5,
97 | },
98 | separator: {
99 | height: StyleSheet.hairlineWidth,
100 | backgroundColor: "white",
101 | },
102 | separatorThin: {
103 | height: StyleSheet.hairlineWidth,
104 | backgroundColor: "#A6A6A6",
105 | },
106 | propertyText: {
107 | fontSize: 12,
108 | color: "#A6A6A6",
109 | textAlign: "left",
110 | },
111 | valueText: {
112 | fontSize: 15,
113 | color: "white",
114 | textAlign: "right",
115 | },
116 | });
117 |
--------------------------------------------------------------------------------
/components/Stock/InfoSlideThree.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { StyleSheet, Text, View } from "react-native";
3 |
4 | export default function InfoSlideThree({ info }) {
5 | return (
6 |
7 |
8 |
9 |
10 | 50 DAYS MA
11 |
12 | {parseFloat(info["day50MovingAvg"]).toFixed(2) || "--"}
13 |
14 |
15 |
16 | 200 DAYS MA
17 |
18 | {parseFloat(info["day200MovingAvg"]).toFixed(2) || "--"}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 5 DAYS CHANGE(%)
27 |
28 | {parseFloat(info["day5ChangePercent"]).toFixed(2) || "--"}
29 |
30 |
31 |
32 | 30 DAYS CHANGE(%)
33 |
34 | {parseFloat(info["day30ChangePercent"]).toFixed(2) || "--"}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 6 MONTH CHANGE(%)
43 |
44 | {parseFloat(info["month6ChangePercent"]).toFixed(2) || "--"}
45 |
46 |
47 |
48 | 1 YEAR CHANGE(%)
49 |
50 | {parseFloat(info["year1ChangePercent"]).toFixed(2) || "--"}
51 |
52 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 | const styles = StyleSheet.create({
60 | container: {
61 | flex: 1,
62 | paddingLeft: 10,
63 | paddingRight: 10,
64 | backgroundColor: "black",
65 | },
66 | nameBlock: {
67 | flex: 1,
68 | paddingTop: 4,
69 | justifyContent: "center",
70 | alignItems: "center",
71 | },
72 | nameText: {
73 | fontWeight: "bold",
74 | fontSize: 12,
75 | color: "white",
76 | },
77 | details: {
78 | flex: 5,
79 | flexDirection: "column",
80 | borderTopWidth: StyleSheet.hairlineWidth,
81 | borderBottomWidth: StyleSheet.hairlineWidth,
82 | borderColor: "white",
83 | },
84 | detailsRow: {
85 | flex: 1,
86 | flexDirection: "row",
87 | alignItems: "center",
88 | justifyContent: "space-between",
89 | },
90 | detailsRowColumn: {
91 | flex: 1,
92 | flexDirection: "row",
93 | alignItems: "center",
94 | justifyContent: "space-between",
95 | paddingLeft: 5,
96 | paddingRight: 5,
97 | },
98 | separator: {
99 | height: StyleSheet.hairlineWidth,
100 | backgroundColor: "white",
101 | },
102 | separatorThin: {
103 | height: StyleSheet.hairlineWidth,
104 | backgroundColor: "#A6A6A6",
105 | },
106 | propertyText: {
107 | fontSize: 12,
108 | color: "#A6A6A6",
109 | textAlign: "left",
110 | },
111 | valueText: {
112 | fontSize: 15,
113 | color: "white",
114 | textAlign: "right",
115 | },
116 | });
117 |
--------------------------------------------------------------------------------
/components/Stock/InfoSlideTwo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { StyleSheet, Text, View } from "react-native";
3 |
4 | export default function InfoSlideTwo({ info }) {
5 | return (
6 |
7 |
8 |
9 |
10 | YIELD
11 |
12 | {info["dividendYield"]
13 | ? parseFloat(info["dividendYield"]).toFixed(2)
14 | : "--"}
15 |
16 |
17 |
18 | MKT CAP
19 |
20 | {info["marketcap"]
21 | ? parseFloat(info["marketcap"] / 1000000000).toFixed(2)
22 | : "--"}{" "}
23 | B
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | EPS(est.)
32 |
33 | {info["ttmEPS"] ? parseFloat(info["ttmEPS"]).toFixed(2) : "--"}
34 |
35 |
36 |
37 | PRICE TO SALE
38 |
39 | {info["priceToSales"]
40 | ? parseFloat(info["priceToSales"]).toFixed(2)
41 | : "--"}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | PRICE TO BOOK
50 |
51 | {info["priceToBook"]
52 | ? parseFloat(info["priceToBook"]).toFixed(2)
53 | : "--"}
54 |
55 |
56 |
57 | PE RATIO
58 |
59 | {info["peRatio"] ? parseFloat(info["peRatio"]).toFixed(2) : "--"}
60 |
61 |
62 |
63 |
64 |
65 |
66 | );
67 | }
68 | const styles = StyleSheet.create({
69 | container: {
70 | flex: 1,
71 | paddingLeft: 10,
72 | paddingRight: 10,
73 | backgroundColor: "black",
74 | },
75 | nameBlock: {
76 | flex: 1,
77 | paddingTop: 4,
78 | justifyContent: "center",
79 | alignItems: "center",
80 | },
81 | nameText: {
82 | fontWeight: "bold",
83 | fontSize: 12,
84 | color: "white",
85 | },
86 | details: {
87 | flex: 5,
88 | flexDirection: "column",
89 | borderTopWidth: StyleSheet.hairlineWidth,
90 | borderBottomWidth: StyleSheet.hairlineWidth,
91 | borderColor: "white",
92 | },
93 | detailsRow: {
94 | flex: 1,
95 | flexDirection: "row",
96 | alignItems: "center",
97 | justifyContent: "space-between",
98 | },
99 | detailsRowColumn: {
100 | flex: 1,
101 | flexDirection: "row",
102 | alignItems: "center",
103 | justifyContent: "space-between",
104 | paddingLeft: 5,
105 | paddingRight: 5,
106 | },
107 | separator: {
108 | height: StyleSheet.hairlineWidth,
109 | backgroundColor: "white",
110 | },
111 | separatorThin: {
112 | height: StyleSheet.hairlineWidth,
113 | backgroundColor: "#A6A6A6",
114 | },
115 | propertyText: {
116 | fontSize: 12,
117 | color: "#A6A6A6",
118 | textAlign: "left",
119 | },
120 | valueText: {
121 | fontSize: 15,
122 | color: "white",
123 | textAlign: "right",
124 | },
125 | });
126 |
--------------------------------------------------------------------------------
/components/Stock/Input.js:
--------------------------------------------------------------------------------
1 | import React, { Component, useState } from "react";
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | ScrollView,
7 | TouchableOpacity,
8 | TextInput,
9 | } from "react-native";
10 | import { Button, Input } from "react-native-elements";
11 | import axios from "axios";
12 |
13 | export default function InputForm({ setSelectedStock, selectedStock }) {
14 | const [searchText, setSearchText] = useState("");
15 |
16 | const fetchStock = async () => {
17 | try {
18 | const res = await axios.get(
19 | `https://sandbox.iexapis.com/stable/stock/${searchText}/chart/1y?token=Tsk_9b260400520a4d23abfe1ef6cb0d3feb`
20 | );
21 | const infoRes = await axios.get(
22 | `https://sandbox.iexapis.com/stable/stock/${searchText}/advanced-stats?token=Tsk_9b260400520a4d23abfe1ef6cb0d3feb`
23 | );
24 | setSelectedStock({
25 | data: res.data,
26 | info: { ...infoRes.data, symbol: searchText.toUpperCase() },
27 | });
28 | } catch (error) {
29 | console.log(error);
30 | }
31 | };
32 |
33 | const handleSubmit = async () => {
34 | searchText && (await fetchStock(searchText));
35 | setSearchText("");
36 | };
37 |
38 | return (
39 |
53 |
54 | {
56 | setSearchText(textEntry);
57 | }}
58 | style={{ backgroundColor: "transparent" }}
59 | onSubmitEditing={() => handleSubmit()}
60 | color="white"
61 | placeholder="Please enter a symbol"
62 | placeholderTextColor="white"
63 | selectionColor="white"
64 | value={searchText}
65 | />
66 |
67 |
68 |
82 |
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/components/StyledText.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Text } from "react-native";
3 |
4 | export function MonoText(props) {
5 | return (
6 |
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/components/TabBarIcon.js:
--------------------------------------------------------------------------------
1 | import { Ionicons } from "@expo/vector-icons";
2 | import * as React from "react";
3 |
4 | import Colors from "../constants/Colors";
5 |
6 | export default function TabBarIcon(props) {
7 | return (
8 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/components/__tests__/StyledText-test.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import renderer from "react-test-renderer";
3 |
4 | import { MonoText } from "../StyledText";
5 |
6 | it(`renders correctly`, () => {
7 | const tree = renderer.create(Snapshot test!).toJSON();
8 |
9 | expect(tree).toMatchSnapshot();
10 | });
11 |
--------------------------------------------------------------------------------
/components/__tests__/__snapshots__/StyledText-test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders correctly 1`] = `
4 |
14 | Snapshot test!
15 |
16 | `;
17 |
--------------------------------------------------------------------------------
/components/sector/SectorChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { StyleSheet, View, Platform } from "react-native";
3 | import Echarts from "native-echarts";
4 | import { useEffect, useState } from "react";
5 | import axios from "axios";
6 | const SectorChart = () => {
7 | const [sectorData, setSectorData] = useState([]);
8 | const fetchSector = async () => {
9 | try {
10 | const sectorRes = await axios.get(
11 | `https://www.alphavantage.co/query?function=SECTOR&apikey=demo`
12 | );
13 | setSectorData(sectorRes.data[`Rank B: 1 Day Performance`]);
14 | } catch (error) {
15 | console.log(error);
16 | }
17 | };
18 |
19 | useEffect(() => {
20 | fetchSector();
21 | }, []);
22 | const name = "Market Change by (%)";
23 | const sectors = Object.keys(sectorData);
24 | const performance = Object.values(sectorData).map((e) =>
25 | parseFloat(e).toFixed(2)
26 | );
27 |
28 | const option = {
29 | tooltip: {
30 | trigger: "axis",
31 | axisPointer: {
32 | type: "shadow",
33 | },
34 | },
35 | legend: {
36 | data: [name],
37 | textStyle: {
38 | color: "white",
39 | },
40 | },
41 | grid: {
42 | left: "1%",
43 | right: "4%",
44 | bottom: "3%",
45 | containLabel: true,
46 | },
47 | xAxis: [
48 | {
49 | type: "value",
50 | axisLine: {
51 | lineStyle: { color: "white" },
52 | },
53 | axisTick: { show: false },
54 | },
55 | ],
56 | yAxis: [
57 | {
58 | type: "category",
59 | axisTick: { show: false },
60 | axisLine: {
61 | lineStyle: { color: "white" },
62 | },
63 | splitLine: { show: false, color: "black" },
64 | data: sectors,
65 | },
66 | ],
67 | series: [
68 | {
69 | name: name,
70 | type: "bar",
71 | label: {
72 | normal: {
73 | show: true,
74 | position: "inside",
75 | },
76 | },
77 | data: performance,
78 | },
79 | ],
80 | };
81 | return (
82 |
83 |
84 |
85 | );
86 | };
87 |
88 | const styles = StyleSheet.create({
89 | container: {
90 | flex: 1,
91 | paddingLeft: 10,
92 | paddingRight: 10,
93 | backgroundColor: "black",
94 | },
95 | nameBlock: {
96 | flex: 1,
97 | paddingTop: 4,
98 | justifyContent: "center",
99 | alignItems: "center",
100 | },
101 | nameText: {
102 | fontWeight: "bold",
103 | fontSize: 12,
104 | color: "white",
105 | },
106 | details: {
107 | flex: 5,
108 | flexDirection: "column",
109 | borderTopWidth: StyleSheet.hairlineWidth,
110 | borderBottomWidth: StyleSheet.hairlineWidth,
111 | borderColor: "white",
112 | },
113 | detailsRow: {
114 | flex: 1,
115 | flexDirection: "row",
116 | alignItems: "center",
117 | justifyContent: "space-between",
118 | marginTop: 10,
119 | },
120 | detailsRowColumn: {
121 | flex: 1,
122 | flexDirection: "row",
123 | alignItems: "center",
124 | justifyContent: "space-between",
125 | paddingLeft: 5,
126 | paddingRight: 5,
127 | },
128 | separator: {
129 | height: StyleSheet.hairlineWidth,
130 | backgroundColor: "white",
131 | marginTop: 20,
132 | },
133 | separatorThin: {
134 | height: StyleSheet.hairlineWidth,
135 | backgroundColor: "#A6A6A6",
136 | },
137 | propertyText: {
138 | fontSize: 12,
139 | color: "#A6A6A6",
140 | textAlign: "left",
141 | },
142 | valueText: {
143 | fontSize: 15,
144 | color: "white",
145 | textAlign: "right",
146 | },
147 | title: {
148 | paddingTop: 5,
149 | color: "white",
150 | fontSize: 15,
151 | fontWeight: "bold",
152 | textAlign: "center",
153 | justifyContent: "center",
154 | alignItems: "center",
155 | },
156 | });
157 |
158 | export default SectorChart;
159 |
--------------------------------------------------------------------------------
/components/stock/Chart.js:
--------------------------------------------------------------------------------
1 | import Echarts from "native-echarts";
2 | import React, { Component } from "react";
3 | import { StyleSheet, Text, View, Platform, Dimensions } from "react-native";
4 |
5 | const { width } = Dimensions.get("window");
6 |
7 | export default function Chart({ selectedStock }) {
8 | const { data, info } = selectedStock;
9 | const calculateMA = (dayCount, data) => {
10 | const result = [];
11 | for (let i = 0, len = data.length; i < len; i++) {
12 | if (i < dayCount) {
13 | result.push("-");
14 | continue;
15 | }
16 | var sum = 0;
17 |
18 | for (let j = 0; j < dayCount; j++) {
19 | sum += data[i - j][1];
20 | }
21 | result.push(sum / dayCount);
22 | }
23 | return result;
24 | };
25 |
26 | let dates = [];
27 | let values = [];
28 |
29 | if (data) {
30 | data.map((stockData) => dates.push(stockData["date"]));
31 | data.map((item) =>
32 | values.push([item["open"], item["close"], item["low"], item["high"]])
33 | );
34 | }
35 | const option = {
36 | backgroundColor: "black",
37 | tooltip: {
38 | trigger: "none",
39 | axisPointer: {
40 | animation: false,
41 | type: "cross",
42 | lineStyle: {
43 | color: "#376df4",
44 | width: 2,
45 | opacity: 1,
46 | },
47 | },
48 | },
49 | legend: {
50 | data: ["MA5", "MA10", "MA20", "MA30"],
51 | inactiveColor: "#777",
52 | textStyle: {
53 | color: "#fff",
54 | },
55 | },
56 | toolbox: {
57 | orient: "vertical",
58 | show: true,
59 | showTitle: true,
60 | },
61 | xAxis: [
62 | {
63 | type: "category",
64 | data: dates,
65 | axisLine: { lineStyle: { color: "#F3F3F3" } },
66 | },
67 | ],
68 | yAxis: [
69 | {
70 | scale: true,
71 | axisLine: {
72 | lineStyle: { color: "#F3F3F3" },
73 | },
74 | splitLine: { show: false },
75 | },
76 | ],
77 | grid: [
78 | {
79 | left: "10%",
80 | right: "8%",
81 | height: "50%",
82 | },
83 | {
84 | left: "10%",
85 | right: "8%",
86 | bottom: "10%",
87 | height: "15%",
88 | },
89 | ],
90 |
91 | dataZoom: [
92 | {
93 | textStyle: {
94 | color: "#F3F3F3",
95 | },
96 | handleIcon:
97 | "M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z",
98 | handleSize: "80%",
99 | dataBackground: {
100 | areaStyle: {
101 | color: "#8392A5",
102 | },
103 | lineStyle: {
104 | opacity: 0.8,
105 | color: "#8392A5",
106 | },
107 | },
108 | handleStyle: {
109 | color: "#fff",
110 | shadowBlur: 3,
111 | shadowColor: "rgba(0, 0, 0, 0.6)",
112 | shadowOffsetX: 2,
113 | shadowOffsetY: 2,
114 | },
115 | },
116 | {
117 | type: "inside",
118 | },
119 | ],
120 | color: ["rgb(249,159,94)", "rgb(67,205,126)"],
121 | animation: true,
122 | series: [
123 | {
124 | type: "candlestick",
125 | name: "Daily",
126 | data: values,
127 | itemStyle: {
128 | normal: {
129 | color: "#FD1050",
130 | color0: "#0CF49B",
131 | borderColor: "#FD1050",
132 | borderColor0: "#0CF49B",
133 | },
134 | },
135 | },
136 | {
137 | name: "MA5",
138 | type: "line",
139 | data: calculateMA(5, values),
140 | smooth: true,
141 | showSymbol: false,
142 | lineStyle: {
143 | normal: {
144 | width: 1,
145 | },
146 | },
147 | },
148 | {
149 | name: "MA10",
150 | type: "line",
151 | data: calculateMA(10, values),
152 | smooth: true,
153 | showSymbol: false,
154 | lineStyle: {
155 | normal: {
156 | width: 1,
157 | },
158 | },
159 | },
160 | {
161 | name: "MA20",
162 | type: "line",
163 | data: calculateMA(20, values),
164 | smooth: true,
165 | showSymbol: false,
166 | lineStyle: {
167 | normal: {
168 | width: 1,
169 | },
170 | },
171 | },
172 | {
173 | name: "MA30",
174 | type: "line",
175 | data: calculateMA(30, values),
176 | smooth: true,
177 | showSymbol: false,
178 | lineStyle: {
179 | normal: {
180 | width: 1,
181 | },
182 | },
183 | },
184 | ],
185 | };
186 |
187 | return (
188 |
189 |
190 | {info && {info["companyName"]}}
191 |
192 |
193 |
194 |
195 | );
196 | }
197 | const styles = StyleSheet.create({
198 | container: {
199 | flex: 1,
200 | },
201 |
202 | titleView: {
203 | height: Platform.OS == "ios" ? 20 : 44,
204 | paddingTop: Platform.OS == "ios" ? 0 : 0,
205 | backgroundColor: "black",
206 | justifyContent: "center",
207 | alignItems: "center",
208 | },
209 | title: {
210 | color: "white",
211 | fontSize: 15,
212 | fontWeight: "bold",
213 | textAlign: "center",
214 | },
215 | });
216 |
--------------------------------------------------------------------------------
/components/stock/Input.js:
--------------------------------------------------------------------------------
1 | import React, { Component, useState } from "react";
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | ScrollView,
7 | TouchableOpacity,
8 | TextInput,
9 | } from "react-native";
10 | import { Button, Input } from "react-native-elements";
11 | import axios from "axios";
12 |
13 | export default function InputForm({ setSelectedStock, selectedStock }) {
14 | const [searchText, setSearchText] = useState("");
15 |
16 | const fetchStock = async () => {
17 | try {
18 | const res = await axios.get(
19 | `https://sandbox.iexapis.com/stable/stock/${searchText}/chart/1y?token=Tsk_9b260400520a4d23abfe1ef6cb0d3feb`
20 | );
21 | const infoRes = await axios.get(
22 | `https://sandbox.iexapis.com/stable/stock/${searchText}/advanced-stats?token=Tsk_9b260400520a4d23abfe1ef6cb0d3feb`
23 | );
24 | setSelectedStock({
25 | data: res.data,
26 | info: { ...infoRes.data, symbol: searchText.toUpperCase() },
27 | });
28 | } catch (error) {
29 | console.log(error);
30 | }
31 | };
32 |
33 | const handleSubmit = async () => {
34 | searchText && (await fetchStock(searchText));
35 | setSearchText("");
36 | };
37 |
38 | return (
39 |
53 |
54 | {
56 | setSearchText(textEntry);
57 | }}
58 | style={{ backgroundColor: "transparent" }}
59 | onSubmitEditing={() => handleSubmit()}
60 | color="white"
61 | placeholder="Please enter a symbol"
62 | placeholderTextColor="white"
63 | selectionColor="white"
64 | value={searchText}
65 | />
66 |
67 |
68 |
82 |
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/constants/Colors.js:
--------------------------------------------------------------------------------
1 | const tintColor = '#2f95dc';
2 |
3 | export default {
4 | tintColor,
5 | tabIconDefault: '#ccc',
6 | tabIconSelected: tintColor,
7 | tabBar: '#fefefe',
8 | errorBackground: 'red',
9 | errorText: '#fff',
10 | warningBackground: '#EAEB5E',
11 | warningText: '#666804',
12 | noticeBackground: tintColor,
13 | noticeText: '#fff',
14 | };
15 |
--------------------------------------------------------------------------------
/constants/Layout.js:
--------------------------------------------------------------------------------
1 | import { Dimensions } from 'react-native';
2 |
3 | const width = Dimensions.get('window').width;
4 | const height = Dimensions.get('window').height;
5 |
6 | export default {
7 | window: {
8 | width,
9 | height,
10 | },
11 | isSmallDevice: width < 375,
12 | };
13 |
--------------------------------------------------------------------------------
/hooks/useCachedResources.js:
--------------------------------------------------------------------------------
1 | import { Ionicons } from '@expo/vector-icons';
2 | import * as Font from 'expo-font';
3 | import * as SplashScreen from 'expo-splash-screen';
4 | import * as React from 'react';
5 |
6 | export default function useCachedResources() {
7 | const [isLoadingComplete, setLoadingComplete] = React.useState(false);
8 |
9 | // Load any resources or data that we need prior to rendering the app
10 | React.useEffect(() => {
11 | async function loadResourcesAndDataAsync() {
12 | try {
13 | SplashScreen.preventAutoHideAsync();
14 |
15 | // Load fonts
16 | await Font.loadAsync({
17 | ...Ionicons.font,
18 | 'space-mono': require('../assets/fonts/SpaceMono-Regular.ttf'),
19 | });
20 | } catch (e) {
21 | // We might want to provide this error information to an error reporting service
22 | console.warn(e);
23 | } finally {
24 | setLoadingComplete(true);
25 | SplashScreen.hideAsync();
26 | }
27 | }
28 |
29 | loadResourcesAndDataAsync();
30 | }, []);
31 |
32 | return isLoadingComplete;
33 | }
34 |
--------------------------------------------------------------------------------
/navigation/BottomTabNavigator.js:
--------------------------------------------------------------------------------
1 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
2 | import * as React from "react";
3 |
4 | import TabBarIcon from "../components/TabBarIcon";
5 |
6 | import MarketOverview from "../screens/MarketOverview";
7 | import SectorScreen from "../screens/SectorScreen";
8 | import StockScreen from "../screens/StockScreen";
9 |
10 | const BottomTab = createBottomTabNavigator();
11 | const INITIAL_ROUTE_NAME = "Home";
12 |
13 | export default function BottomTabNavigator({ navigation, route }) {
14 | // Set the header title on the parent stack navigator depending on the
15 | // currently active tab. Learn more in the documentation:
16 | // https://reactnavigation.org/docs/en/screen-options-resolution.html
17 | navigation.setOptions({ headerTitle: getHeaderTitle(route) });
18 |
19 | return (
20 |
21 | (
27 |
28 | ),
29 | }}
30 | />
31 | (
37 |
38 | ),
39 | }}
40 | />
41 | (
47 |
48 | ),
49 | }}
50 | />
51 | {/* need find a API can grab Crypto currency information
52 | (
58 |
62 | ),
63 | }}
64 | /> */}
65 |
66 | );
67 | }
68 |
69 | function getHeaderTitle(route) {
70 | const routeName =
71 | route.state?.routes[route.state.index]?.name ?? INITIAL_ROUTE_NAME;
72 |
73 | switch (routeName) {
74 | case "Home":
75 | return "Market Overall";
76 | case "Stock":
77 | return "Stock";
78 | case "Sector":
79 | return "Market by Sector";
80 | case "Crypto":
81 | return "Crypto Market";
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/navigation/LinkingConfiguration.js:
--------------------------------------------------------------------------------
1 | import * as Linking from "expo-linking";
2 |
3 | export default {
4 | prefixes: [Linking.makeUrl("/")],
5 | config: {
6 | Root: {
7 | path: "root",
8 | screens: {
9 | Home: "home",
10 | Links: "links",
11 | },
12 | },
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject",
9 | "test": "jest --watchAll"
10 | },
11 | "jest": {
12 | "preset": "jest-expo"
13 | },
14 | "dependencies": {
15 | "@expo/vector-icons": "~10.0.6",
16 | "@react-native-community/masked-view": "0.1.6",
17 | "@react-navigation/bottom-tabs": "^5.3.1",
18 | "@react-navigation/native": "^5.2.1",
19 | "@react-navigation/stack": "^5.2.16",
20 | "axios": "^0.19.2",
21 | "expo": "~37.0.9",
22 | "expo-asset": "~8.1.0",
23 | "expo-constants": "~9.0.0",
24 | "expo-font": "~8.1.0",
25 | "expo-linking": "^1.0.1",
26 | "expo-splash-screen": "^0.2.3",
27 | "expo-web-browser": "~8.2.0",
28 | "native-echarts": "^0.5.0",
29 | "react": "~16.9.0",
30 | "react-dom": "~16.9.0",
31 | "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
32 | "react-native-best-viewpager": "^1.0.4",
33 | "react-native-elements": "^2.0.0",
34 | "react-native-gesture-handler": "~1.6.0",
35 | "react-native-safe-area-context": "0.7.3",
36 | "react-native-screens": "~2.2.0",
37 | "react-native-vector-icons": "^6.6.0",
38 | "react-native-web": "~0.11.7",
39 | "react-native-webview": "^10.1.1",
40 | "rn-viewpager": "^1.2.9"
41 | },
42 | "devDependencies": {
43 | "@babel/core": "^7.8.6",
44 | "babel-preset-expo": "~8.1.0",
45 | "jest-expo": "~37.0.0"
46 | },
47 | "private": true
48 | }
49 |
--------------------------------------------------------------------------------
/screens/MarketOverview.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import * as React from "react";
3 | import { useEffect, useState } from "react";
4 | import { StyleSheet, View } from "react-native";
5 | import { Card, Divider, Header } from "react-native-elements";
6 | import { ScrollView } from "react-native-gesture-handler";
7 | import StockChart from "../components/Home/StockChart";
8 |
9 | export default function MarketOverview() {
10 | const [dowData, setDowData] = useState([]);
11 | const [nasdqaData, setNasdqaData] = useState([]);
12 | const [sp500Data, setSp500Data] = useState([]);
13 | const initialLoading = async () => {
14 | try {
15 | const dowRes = await axios.get(
16 | `https://sandbox.iexapis.com/stable/stock/DIA/chart?token=Tsk_9b260400520a4d23abfe1ef6cb0d3feb`
17 | );
18 | const nasdqaRes = await axios.get(
19 | `https://sandbox.iexapis.com/stable/stock/qqq/chart?token=Tsk_9b260400520a4d23abfe1ef6cb0d3feb`
20 | );
21 |
22 | const sp500Res = await axios.get(
23 | `https://sandbox.iexapis.com/stable/stock/spy/chart?token=Tsk_9b260400520a4d23abfe1ef6cb0d3feb`
24 | );
25 |
26 | setDowData(dowRes.data);
27 | setNasdqaData(nasdqaRes.data);
28 | setSp500Data(sp500Res.data);
29 | } catch (error) {
30 | console.log(error);
31 | }
32 | };
33 |
34 | useEffect(() => {
35 | initialLoading();
36 | }, []);
37 | return (
38 |
39 |
40 |
49 |
50 |
55 |
56 | {dowData.length > 0 && (
57 |
58 | )}
59 |
60 | {nasdqaData.length > 0 && (
61 |
62 | )}
63 |
64 | {sp500Data.length > 0 && (
65 |
66 | )}
67 |
68 |
69 |
70 |
71 |
72 | );
73 | }
74 |
75 | const styles = StyleSheet.create({
76 | container: {
77 | flex: 1,
78 | paddingLeft: 10,
79 | paddingRight: 10,
80 | backgroundColor: "black",
81 | },
82 | nameBlock: {
83 | flex: 1,
84 | paddingTop: 4,
85 | justifyContent: "center",
86 | alignItems: "center",
87 | },
88 | nameText: {
89 | fontWeight: "bold",
90 | fontSize: 12,
91 | color: "white",
92 | },
93 | details: {
94 | flex: 5,
95 | flexDirection: "column",
96 | borderTopWidth: StyleSheet.hairlineWidth,
97 | borderBottomWidth: StyleSheet.hairlineWidth,
98 | borderColor: "white",
99 | },
100 | detailsRow: {
101 | flex: 1,
102 | flexDirection: "row",
103 | alignItems: "center",
104 | justifyContent: "space-between",
105 | marginTop: 10,
106 | },
107 | detailsRowColumn: {
108 | flex: 1,
109 | flexDirection: "row",
110 | alignItems: "center",
111 | justifyContent: "space-between",
112 | paddingLeft: 5,
113 | paddingRight: 5,
114 | },
115 | separator: {
116 | height: StyleSheet.hairlineWidth,
117 | backgroundColor: "white",
118 | marginTop: 20,
119 | },
120 | separatorThin: {
121 | height: StyleSheet.hairlineWidth,
122 | backgroundColor: "#A6A6A6",
123 | },
124 | propertyText: {
125 | fontSize: 12,
126 | color: "#A6A6A6",
127 | textAlign: "left",
128 | },
129 | valueText: {
130 | fontSize: 15,
131 | color: "white",
132 | textAlign: "right",
133 | },
134 | title: {
135 | paddingTop: 5,
136 | color: "white",
137 | fontSize: 15,
138 | fontWeight: "bold",
139 | textAlign: "center",
140 | justifyContent: "center",
141 | alignItems: "center",
142 | },
143 | });
144 |
--------------------------------------------------------------------------------
/screens/SectorScreen.js:
--------------------------------------------------------------------------------
1 | import { Ionicons } from "@expo/vector-icons";
2 | import * as WebBrowser from "expo-web-browser";
3 | import * as React from "react";
4 |
5 | import { StyleSheet, Text, View } from "react-native";
6 | import { RectButton, ScrollView } from "react-native-gesture-handler";
7 | import { Header, Card, Divider } from "react-native-elements";
8 | import SectorChart from "../components/Sector/SectorChart";
9 |
10 | import { useState, useEffect } from "react";
11 |
12 | export default function MarketOverview() {
13 | return (
14 |
15 |
16 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 |
41 | const styles = StyleSheet.create({
42 | container: {
43 | flex: 1,
44 | paddingLeft: 10,
45 | paddingRight: 10,
46 | backgroundColor: "black",
47 | },
48 | nameBlock: {
49 | flex: 1,
50 | paddingTop: 4,
51 | justifyContent: "center",
52 | alignItems: "center",
53 | },
54 | nameText: {
55 | fontWeight: "bold",
56 | fontSize: 12,
57 | color: "white",
58 | },
59 | details: {
60 | flex: 5,
61 | flexDirection: "column",
62 | borderTopWidth: StyleSheet.hairlineWidth,
63 | borderBottomWidth: StyleSheet.hairlineWidth,
64 | borderColor: "white",
65 | },
66 | detailsRow: {
67 | flex: 1,
68 | flexDirection: "row",
69 | alignItems: "center",
70 | justifyContent: "space-between",
71 | marginTop: 10,
72 | },
73 | detailsRowColumn: {
74 | flex: 1,
75 | flexDirection: "row",
76 | alignItems: "center",
77 | justifyContent: "space-between",
78 | paddingLeft: 5,
79 | paddingRight: 5,
80 | },
81 | separator: {
82 | height: StyleSheet.hairlineWidth,
83 | backgroundColor: "white",
84 | marginTop: 20,
85 | },
86 | separatorThin: {
87 | height: StyleSheet.hairlineWidth,
88 | backgroundColor: "#A6A6A6",
89 | },
90 | propertyText: {
91 | fontSize: 12,
92 | color: "#A6A6A6",
93 | textAlign: "left",
94 | },
95 | valueText: {
96 | fontSize: 15,
97 | color: "white",
98 | textAlign: "right",
99 | },
100 | title: {
101 | paddingTop: 5,
102 | color: "white",
103 | fontSize: 15,
104 | fontWeight: "bold",
105 | textAlign: "center",
106 | justifyContent: "center",
107 | alignItems: "center",
108 | },
109 | });
110 |
--------------------------------------------------------------------------------
/screens/StockScreen.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { useEffect, useState } from "react";
3 | import * as React from "react";
4 | import {
5 | Linking,
6 | TextInput,
7 | Platform,
8 | Text,
9 | TouchableHighlight,
10 | StyleSheet,
11 | View,
12 | } from "react-native";
13 | import Icon from "react-native-vector-icons/MaterialIcons";
14 | import {
15 | IndicatorViewPager,
16 | PagerDotIndicator,
17 | } from "react-native-best-viewpager";
18 | import Input from "../components/Stock/Input";
19 | import Chart from "../components/Stock/Chart";
20 | import InfoSlideOne from "../components/Stock/InfoSlideOne";
21 | import InfoSlideTwo from "../components/Stock/InfoSlideTwo";
22 | import InfoSlideThree from "../components/Stock/InfoSlideThree";
23 |
24 | export default function StockScreen() {
25 | const [selectedStock, setSelectedStock] = useState({ data: [], info: null });
26 | return (
27 |
28 | {Platform.OS === "ios" && }
29 |
30 |
34 |
35 |
36 |
37 | }
40 | >
41 |
42 | {selectedStock.info && }
43 |
44 |
45 | {selectedStock.info && }
46 |
47 |
48 | {selectedStock.info && }
49 |
50 |
51 |
52 |
53 | console.log("will add press function")}
56 | >
57 | {/* underlayColor="#202020" */}
58 |
59 | {selectedStock.info && selectedStock.info["symbol"]}
60 |
61 |
62 |
63 |
64 |
65 | {selectedStock.info &&
66 | `BETA: ${parseFloat(selectedStock.info["beta"]).toFixed(2)}`}
67 |
68 |
69 | console.log("Need also something")}
72 | underlayColor="#202020"
73 | >
74 |
75 |
76 |
77 |
78 | );
79 | }
80 | const styles = StyleSheet.create({
81 | container: {
82 | flex: 1,
83 | justifyContent: "space-between",
84 | backgroundColor: "black",
85 | },
86 | statusBar: {
87 | height: 10,
88 | },
89 | stocksBlock: {
90 | flexDirection: "column",
91 | marginBottom: 10,
92 | flex: 9,
93 | },
94 | detailedBlock: {
95 | flex: 5,
96 | backgroundColor: "#202020",
97 | justifyContent: "space-between",
98 | },
99 | footerBlock: {
100 | flex: 1,
101 | flexDirection: "row",
102 | backgroundColor: "#202020",
103 | alignItems: "center",
104 | paddingLeft: 10,
105 | paddingRight: 10,
106 | },
107 | loadingText: {
108 | fontSize: 15,
109 | textAlign: "center",
110 | marginTop: 40,
111 | marginBottom: 10,
112 | marginRight: 10,
113 | color: "white",
114 | },
115 | finance: {
116 | flex: 1,
117 | },
118 | financeText: {
119 | fontSize: 15,
120 | fontWeight: "bold",
121 | color: "white",
122 | textAlign: "left",
123 | },
124 | footerMiddle: {
125 | flex: 1,
126 | },
127 | marketTimeText: {
128 | fontSize: 12,
129 | color: "#A6A6A6",
130 | textAlign: "center",
131 | },
132 | settings: {
133 | flex: 1,
134 | alignItems: "flex-end",
135 | },
136 | icon: {
137 | width: 20,
138 | height: 20,
139 | },
140 | });
141 |
--------------------------------------------------------------------------------
/screens/utils.js:
--------------------------------------------------------------------------------
1 | export const convertStockData = (res) => {
2 | let result = [];
3 |
4 | if (res) {
5 | const data = res.data["Time Series (Daily)"];
6 |
7 | Object.entries(data).map((item) => result.push(item));
8 | }
9 |
10 | return result;
11 | };
12 |
13 | export const convertCryptoData = (data) => {
14 | let crypto = { name: "crypto", value: 0 };
15 | data = data
16 | .map((e) => {
17 | return {
18 | name: e["companyName"].slice(0, -3),
19 | value: parseFloat(
20 | (e["bidPrice"] * e["latestVolume"]) / 1000000
21 | ).toFixed(2),
22 | };
23 | })
24 | .filter((e) => e.value > 0);
25 | crypto["children"] = data;
26 | crypto.value = parseFloat(
27 | data.reduce((sum, cur) => sum + Number(cur.value), 0)
28 | ).toFixed(2);
29 |
30 | return [crypto];
31 | };
32 |
--------------------------------------------------------------------------------