diff --git a/app/models/backtest.py b/app/models/backtest.py
index 7ca994d..cd0c5f7 100644
--- a/app/models/backtest.py
+++ b/app/models/backtest.py
@@ -8,7 +8,7 @@ class Backtest(db.Model):
start_date = db.Column(db.Date)
end_date = db.Column(db.Date)
inital_cash = db.Column(db.Integer)
- fee = db.Column(db.Integer)
+ fee = db.Column(db.Float)
# status = db.Column(db.String(50))
created_at = db.Column(db.DateTime, default=db.func.current_timestamp())
@@ -36,6 +36,7 @@ class Result(db.Model):
losing_trades = db.Column(db.Integer)
max_drawdown = db.Column(db.Numeric(10, 2))
sharpe_ratio = db.Column(db.Numeric(10, 2))
+ is_best = db.Column(db.Boolean, default=False, nullable=True)
class Metric(db.Model):
__tablename__ = 'metrics'
diff --git a/app/routes/backtest.py b/app/routes/backtest.py
index d4ac9a1..15457c7 100644
--- a/app/routes/backtest.py
+++ b/app/routes/backtest.py
@@ -80,7 +80,8 @@ def get_backtest_results(backtest_id):
'winning_trades': result.winning_trades,
'losing_trades': result.losing_trades,
'max_drawdown': float(result.max_drawdown),
- 'sharpe_ratio': float(result.sharpe_ratio)
+ 'sharpe_ratio': float(result.sharpe_ratio),
+ 'is_best': result.is_best
})
return jsonify({'results': result_list}), 200
diff --git a/app/services/backtest_service.py b/app/services/backtest_service.py
index c219455..abe2cca 100644
--- a/app/services/backtest_service.py
+++ b/app/services/backtest_service.py
@@ -41,19 +41,16 @@ def run_and_evaluate_backtest(backtest_id, symbol, initial_cash, fee, start_date
]
results = []
+ result_objects = []
for strategy in strategies:
-
print(strategy, symbol, initial_cash, fee, start_date, end_date)
result = run_backtest(strategy, symbol, initial_cash, fee, start_date, end_date)
result['backtest_id'] = backtest_id
- if(strategy == RsiBollingerBandsStrategy):
- result['strategy'] = 'RsiBollingerBandsStrategy'
- elif (strategy == MacdStrategy):
- result['strategy'] = 'MacdStrategy'
- elif(strategy == StochasticOscillatorStrategy):
- result['strategy'] = 'StochasticOscillatorStrategy'
+ result['strategy'] = strategy.__name__
+
result_obj = Result(**result)
db.session.add(result_obj)
+ result_objects.append(result_obj)
metrics = {
"total_return": result['total_return'],
@@ -65,11 +62,11 @@ def run_and_evaluate_backtest(backtest_id, symbol, initial_cash, fee, start_date
}
mlflow_service.log_metrics(run_name=f"Backtest_{backtest_id}", metrics=metrics)
- # Uncomment to publish results to Kafka
- # kafka_service.produce('backtest_results', {
- # "backtest_id": backtest_id,
- # "metrics": metrics
- # })
+ # publish results to Kafka
+ kafka_service.produce('backtest_results', {
+ "backtest_id": backtest_id,
+ "metrics": metrics
+ })
db.session.commit()
results.append(result)
@@ -84,10 +81,14 @@ def run_and_evaluate_backtest(backtest_id, symbol, initial_cash, fee, start_date
scores = [score_backtest(result, min_return, max_return, min_sharpe, max_sharpe, min_drawdown, max_drawdown) for result in results]
best_strategy_index = scores.index(max(scores))
- best_strategy = strategies[best_strategy_index]
+ for idx, result_obj in enumerate(result_objects):
+ result_obj.is_best = (idx == best_strategy_index)
+
+ db.session.commit()
+
print("Best Strategy:")
- print(best_strategy.__name__)
+ print(strategies[best_strategy_index].__name__)
print("Score:")
print(scores[best_strategy_index])
print("Metrics:")
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 9877a00..18e35e1 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -8,12 +8,17 @@
"name": "frontend",
"version": "0.1.0",
"dependencies": {
+ "@emotion/react": "^11.11.4",
+ "@emotion/styled": "^11.11.5",
+ "@mui/icons-material": "^5.15.20",
+ "@mui/material": "^5.15.20",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-icons": "^5.2.1",
"react-scripts": "5.0.1",
"react-toastify": "^10.0.5",
"web-vitals": "^2.1.4"
@@ -2462,6 +2467,179 @@
"postcss-selector-parser": "^6.0.10"
}
},
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.11.0",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
+ "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.1",
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/serialize": "^1.1.2",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.11.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
+ "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/sheet": "^1.2.2",
+ "@emotion/utils": "^1.2.1",
+ "@emotion/weak-memoize": "^0.3.1",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
+ "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+ "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.11.4",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
+ "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.11.0",
+ "@emotion/cache": "^11.11.0",
+ "@emotion/serialize": "^1.1.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
+ "@emotion/utils": "^1.2.1",
+ "@emotion/weak-memoize": "^0.3.1",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
+ "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.1",
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/unitless": "^0.8.1",
+ "@emotion/utils": "^1.2.1",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
+ "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.11.5",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz",
+ "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.11.0",
+ "@emotion/is-prop-valid": "^1.2.2",
+ "@emotion/serialize": "^1.1.4",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
+ "@emotion/utils": "^1.2.1"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
+ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
+ "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
+ "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
+ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==",
+ "license": "MIT"
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -2563,6 +2741,44 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.3.tgz",
+ "integrity": "sha512-1ZpCvYf788/ZXOhRQGFxnYQOVgeU+pi0i+d0Ow34La7qjIXETi6RNswGVKkA6KcDO8/+Ysu2E/CeUmmeEBDvTg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.3"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.6.tgz",
+ "integrity": "sha512-qiTYajAnh3P+38kECeffMSQgbvXty2VB6rS+42iWR4FPIlZjLK84E9qtLnMTLIpPz2znD/TaFqaiavMUrS+Hcw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.0.0",
+ "@floating-ui/utils": "^0.2.3"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz",
+ "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.3.tgz",
+ "integrity": "sha512-XGndio0l5/Gvd6CLIABvsav9HHezgDFFhDfHk1bvLfr9ni8dojqLSvBbotJEjmIwNHL7vK4QzBJTdBRoB+c1ww==",
+ "license": "MIT"
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -3442,6 +3658,272 @@
"integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
"license": "MIT"
},
+ "node_modules/@mui/base": {
+ "version": "5.0.0-beta.40",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
+ "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@floating-ui/react-dom": "^2.0.8",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "@popperjs/core": "^2.11.8",
+ "clsx": "^2.1.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/core-downloads-tracker": {
+ "version": "5.15.20",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.20.tgz",
+ "integrity": "sha512-DoL2ppgldL16utL8nNyj/P12f8mCNdx/Hb/AJnX9rLY4b52hCMIx1kH83pbXQ6uMy6n54M3StmEbvSGoj2OFuA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ }
+ },
+ "node_modules/@mui/icons-material": {
+ "version": "5.15.20",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.20.tgz",
+ "integrity": "sha512-oGcKmCuHaYbAAoLN67WKSXtHmEgyWcJToT1uRtmPyxMj9N5uqwc/mRtEnst4Wj/eGr+zYH2FiZQ79v9k7kSk1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@mui/material": "^5.0.0",
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material": {
+ "version": "5.15.20",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.20.tgz",
+ "integrity": "sha512-tVq3l4qoXx/NxUgIx/x3lZiPn/5xDbdTE8VrLczNpfblLYZzlrbxA7kb9mI8NoBF6+w9WE9IrxWnKK5KlPI2bg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/base": "5.0.0-beta.40",
+ "@mui/core-downloads-tracker": "^5.15.20",
+ "@mui/system": "^5.15.20",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.20",
+ "@types/react-transition-group": "^4.4.10",
+ "clsx": "^2.1.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/@mui/private-theming": {
+ "version": "5.15.20",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.20.tgz",
+ "integrity": "sha512-BK8F94AIqSrnaPYXf2KAOjGZJgWfvqAVQ2gVR3EryvQFtuBnG6RwodxrCvd3B48VuMy6Wsk897+lQMUxJyk+6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/utils": "^5.15.20",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/styled-engine": {
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz",
+ "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@emotion/cache": "^11.11.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system": {
+ "version": "5.15.20",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.20.tgz",
+ "integrity": "sha512-LoMq4IlAAhxzL2VNUDBTQxAb4chnBe8JvRINVNDiMtHE2PiPOoHlhOPutSxEbaL5mkECPVWSv6p8JEV+uykwIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/private-theming": "^5.15.20",
+ "@mui/styled-engine": "^5.15.14",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.20",
+ "clsx": "^2.1.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/types": {
+ "version": "7.2.14",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz",
+ "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils": {
+ "version": "5.15.20",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.20.tgz",
+ "integrity": "sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@types/prop-types": "^15.7.11",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@@ -3566,6 +4048,16 @@
}
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -4781,6 +5273,15 @@
"@types/react": "*"
}
},
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.10",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
+ "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/resolve": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@@ -7678,6 +8179,16 @@
"utila": "~0.4"
}
},
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -9210,6 +9721,12 @@
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -9932,6 +10449,21 @@
"he": "bin/he"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -16286,6 +16818,15 @@
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==",
"license": "MIT"
},
+ "node_modules/react-icons": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz",
+ "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -16387,6 +16928,22 @@
"react-dom": ">=18"
}
},
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -17873,6 +18430,12 @@
"postcss": "^8.2.15"
}
},
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
"node_modules/sucrase": {
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index b46682a..0380667 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -3,12 +3,17 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@emotion/react": "^11.11.4",
+ "@emotion/styled": "^11.11.5",
+ "@mui/icons-material": "^5.15.20",
+ "@mui/material": "^5.15.20",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-icons": "^5.2.1",
"react-scripts": "5.0.1",
"react-toastify": "^10.0.5",
"web-vitals": "^2.1.4"
diff --git a/frontend/src/BacktestResultModal.js b/frontend/src/BacktestResultModal.js
index 761583a..50c3888 100644
--- a/frontend/src/BacktestResultModal.js
+++ b/frontend/src/BacktestResultModal.js
@@ -1,5 +1,8 @@
import React, { useState, useEffect } from 'react';
+import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa';
import axios from 'axios';
+import { Modal, Box, Typography, IconButton, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from '@mui/material';
+import { Close as CloseIcon } from '@mui/icons-material';
const API_BASE_URL = 'http://localhost:5000';
@@ -24,43 +27,80 @@ const BacktestResultModal = ({ backtest, onClose, token }) => {
}, [backtest.id, token]);
return (
-
-
-
{backtest.name} Details
-
-
-
-
- Strategy |
- Total Return |
- Number of Trades |
- Winning Trades |
- Losing Trades |
- Max Drawdown |
- Sharpe Ratio |
-
-
-
- {results.map((result) => (
-
- {result.strategy} |
- {result.total_return} |
- {result.number_of_trades} |
- {result.winning_trades} |
- {result.losing_trades} |
- {result.max_drawdown} |
- {result.sharpe_ratio} |
-
- ))}
-
-
-
-
+
+
+
+ {backtest.name} Details
+ theme.palette.grey[500],
+ }}
+ >
+
+
+
+
+
+
+
+ Strategy
+ Total Return
+ Number of Trades
+ Winning Trades
+ Losing Trades
+ Max Drawdown
+ Sharpe Ratio
+ Is Best
+
+
+
+ {results.map((result) => (
+
+ {result.strategy}
+ {result.total_return}
+ {result.number_of_trades}
+ {result.winning_trades}
+ {result.losing_trades}
+ {result.max_drawdown}
+ {result.sharpe_ratio}
+
+ {result.is_best ? (
+
+
+ Best
+
+ ) : (
+
+
+ Not the Best
+
+ )}
+
+
+ ))}
+
+
+
+
+
);
};
diff --git a/frontend/src/BacktestResultsTable.js b/frontend/src/BacktestResultsTable.js
index 3d9144c..4ae0b18 100644
--- a/frontend/src/BacktestResultsTable.js
+++ b/frontend/src/BacktestResultsTable.js
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
+import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Typography, Box } from '@mui/material';
import BacktestResultModal from './BacktestResultModal';
const API_BASE_URL = 'http://localhost:5000';
@@ -34,36 +35,44 @@ const BacktestResultsTable = ({ token }) => {
};
return (
-
-
Backtest Results
-
-
-
- Name |
- Symbol |
- Start Date |
- End Date |
- Initial Cash |
- Fee |
-
-
-
- {backtests.map((backtest) => (
- handleRowClick(backtest)} className="cursor-pointer">
- {backtest.name} |
- {backtest.symbol} |
- {backtest.start_date} |
- {backtest.end_date} |
- {backtest.inital_cash} |
- {backtest.fee} |
-
- ))}
-
-
+
+
+ Backtest Results
+
+
+
+
+
+ Name
+ Symbol
+ Start Date
+ End Date
+ Initial Cash
+ Fee
+
+
+
+ {backtests.map((backtest) => (
+ handleRowClick(backtest)}
+ sx={{ cursor: 'pointer', '&:hover': { backgroundColor: '#f5f5f5' } }}
+ >
+ {backtest.name}
+ {backtest.symbol}
+ {backtest.start_date}
+ {backtest.end_date}
+ {backtest.inital_cash}
+ {backtest.fee}
+
+ ))}
+
+
+
{selectedBacktest && (
)}
-
+
);
};