Add indicators to backtest and bot (#14)
* Add indicators to backtest and bot * Remove
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import useTheme from '../../../hooks/useTheme'
|
||||
const themes = ['black', 'coffee', 'cyberpunk', 'lofi', 'retro']
|
||||
const themes = ['black', 'coffee', 'cyberpunk', 'lofi', 'retro', 'kaigen']
|
||||
|
||||
const ThemeSelector = (): JSX.Element => {
|
||||
const { setTheme } = useTheme()
|
||||
|
||||
@@ -139,6 +139,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
|
||||
positions={backtest.positions}
|
||||
walletBalances={backtest.walletBalances}
|
||||
signals={backtest.signals}
|
||||
strategiesValues={backtest.strategiesValues}
|
||||
width={720}
|
||||
height={512}
|
||||
></TradeChart>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { TradeChart, CardPositionItem } from '..'
|
||||
import type { IBotRowDetails } from '../../../global/interface'
|
||||
import { IBotRowDetails } from '../../../global/type'
|
||||
import { CardPosition } from '../../mollecules'
|
||||
|
||||
const BacktestRowDetails: React.FC<IBotRowDetails> = ({
|
||||
candles,
|
||||
positions,
|
||||
walletBalances,
|
||||
strategiesValues,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@@ -30,11 +31,12 @@ const BacktestRowDetails: React.FC<IBotRowDetails> = ({
|
||||
<div>
|
||||
<figure>
|
||||
<TradeChart
|
||||
width={1000}
|
||||
height={500}
|
||||
width={1400}
|
||||
height={1400}
|
||||
candles={candles}
|
||||
positions={positions}
|
||||
walletBalances={walletBalances}
|
||||
strategiesValues={strategiesValues}
|
||||
signals={[]}
|
||||
></TradeChart>
|
||||
</figure>
|
||||
|
||||
@@ -247,6 +247,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching }) => {
|
||||
candles={row.original.candles}
|
||||
positions={row.original.positions}
|
||||
walletBalances={row.original.walletBalances}
|
||||
strategiesValues={row.original.strategiesValues}
|
||||
></BacktestRowDetails>
|
||||
</>
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
BaselineSeriesOptions,
|
||||
CandlestickData,
|
||||
IChartApi,
|
||||
ISeriesApi,
|
||||
@@ -18,6 +19,8 @@ import type {
|
||||
KeyValuePairOfDateTimeAndDecimal,
|
||||
Position,
|
||||
Signal,
|
||||
StrategiesResultBase,
|
||||
StrategyType,
|
||||
} from '../../../../generated/ManagingApi'
|
||||
import {
|
||||
PositionStatus,
|
||||
@@ -25,11 +28,27 @@ import {
|
||||
} from '../../../../generated/ManagingApi'
|
||||
import useTheme from '../../../../hooks/useTheme'
|
||||
|
||||
// var customTheme = {
|
||||
// background: '#0B0B0B',
|
||||
// neutral: '#151515',
|
||||
// primary: '#54B5F9',
|
||||
// secondary: '#C492B1',
|
||||
// third: '#B0DB43',
|
||||
// fourth: '#F2D398',
|
||||
// fifth: '#99EDCC',
|
||||
// red: '#FF5340',
|
||||
// green: '#08C25F',
|
||||
// orange: '#EB6F22',
|
||||
// }
|
||||
|
||||
|
||||
|
||||
type ITradeChartProps = {
|
||||
candles: Candle[]
|
||||
positions: Position[]
|
||||
signals: Signal[]
|
||||
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
|
||||
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
|
||||
stream?: Candle | null
|
||||
width: number
|
||||
height: number
|
||||
@@ -40,6 +59,7 @@ const TradeChart = ({
|
||||
positions,
|
||||
signals,
|
||||
walletBalances,
|
||||
strategiesValues,
|
||||
stream,
|
||||
width,
|
||||
height,
|
||||
@@ -68,6 +88,12 @@ const TradeChart = ({
|
||||
}
|
||||
}
|
||||
|
||||
const baselineOptions: BaselineSeriesOptions = {
|
||||
bottomLineColor: theme.secondary,
|
||||
topLineColor: theme.primary,
|
||||
lineWidth: 1,
|
||||
} as BaselineSeriesOptions
|
||||
|
||||
function buildMarker(
|
||||
shape: SeriesMarkerShape,
|
||||
color: string,
|
||||
@@ -142,7 +168,7 @@ const TradeChart = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (chartRef.current) {
|
||||
const lineColor = theme.secondary
|
||||
const lineColor = theme['base-100']
|
||||
chart.current = createChart(chartRef.current, {
|
||||
crosshair: {
|
||||
mode: CrosshairMode.Normal,
|
||||
@@ -159,8 +185,8 @@ const TradeChart = ({
|
||||
},
|
||||
height: height,
|
||||
layout: {
|
||||
background: {color: '#121212'},
|
||||
textColor: theme.secondary,
|
||||
background: {color: theme['base-300']},
|
||||
textColor: theme.accent,
|
||||
},
|
||||
localization: {
|
||||
dateFormat: 'yyyy-MM-dd',
|
||||
@@ -198,11 +224,11 @@ const TradeChart = ({
|
||||
if (!chart.current) return
|
||||
|
||||
series1.current = chart.current.addCandlestickSeries({
|
||||
borderDownColor: theme.secondary,
|
||||
borderDownColor: theme.accent,
|
||||
borderUpColor: theme.primary,
|
||||
downColor: theme.secondary,
|
||||
downColor: theme.accent,
|
||||
upColor: theme.primary,
|
||||
wickDownColor: theme.secondary,
|
||||
wickDownColor: theme.accent,
|
||||
wickUpColor: theme.primary,
|
||||
})
|
||||
|
||||
@@ -230,7 +256,6 @@ const TradeChart = ({
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
const markers: SeriesMarker<Time>[] = []
|
||||
|
||||
if (signals) {
|
||||
@@ -271,20 +296,268 @@ const TradeChart = ({
|
||||
}
|
||||
}
|
||||
|
||||
// Price panel
|
||||
if (strategiesValues?.EmaTrend != null || strategiesValues?.EmaCross != null)
|
||||
{
|
||||
const emaSeries = chart.current.addLineSeries({
|
||||
color: theme.secondary,
|
||||
lineWidth: 1,
|
||||
priceLineVisible: true,
|
||||
priceLineWidth: 1,
|
||||
priceFormat: {
|
||||
minMove: 0.0001,
|
||||
precision: 4,
|
||||
type: 'price',
|
||||
},
|
||||
title: 'EMA',
|
||||
})
|
||||
|
||||
const ema = strategiesValues.EmaTrend?.ema ?? strategiesValues.EmaCross?.ema
|
||||
|
||||
const emaData = ema?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.ema,
|
||||
}
|
||||
})
|
||||
|
||||
if (emaData != null)
|
||||
{
|
||||
// @ts-ignore
|
||||
emaSeries.setData(emaData)
|
||||
}
|
||||
}
|
||||
|
||||
if (strategiesValues?.SuperTrend != null) {
|
||||
const superTrendSeries = chart.current.addLineSeries({
|
||||
color: theme.info,
|
||||
lineWidth: 1,
|
||||
priceLineVisible: false,
|
||||
priceLineWidth: 1,
|
||||
priceLineColor: theme.info,
|
||||
title: 'SuperTrend',
|
||||
pane: 0,
|
||||
})
|
||||
|
||||
const superTrend = strategiesValues.SuperTrend.superTrend?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.superTrend,
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
superTrendSeries.setData(superTrend)
|
||||
}
|
||||
|
||||
if (markers.length > 0) {
|
||||
series1.current.setMarkers(markers)
|
||||
}
|
||||
|
||||
// Indicator panel
|
||||
var paneCount = 1
|
||||
|
||||
|
||||
if (strategiesValues?.RsiDivergence != null || strategiesValues?.RsiDivergenceConfirm != null)
|
||||
{
|
||||
const rsiSeries = chart.current.addLineSeries({
|
||||
pane: paneCount,
|
||||
title: 'RSI',
|
||||
})
|
||||
|
||||
const rsi = strategiesValues.RsiDivergence?.rsi ?? strategiesValues.RsiDivergenceConfirm?.rsi
|
||||
|
||||
const rsiData = rsi?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.rsi,
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
rsiSeries.setData(rsiData)
|
||||
rsiSeries.applyOptions({
|
||||
...baselineOptions,
|
||||
priceFormat: {
|
||||
minMove: 0.01,
|
||||
precision: 4,
|
||||
type: 'price',
|
||||
},
|
||||
})
|
||||
paneCount++
|
||||
}
|
||||
|
||||
if (strategiesValues?.Stc != null) {
|
||||
const stcSeries = chart.current.addBaselineSeries({
|
||||
pane: paneCount,
|
||||
baseValue: {price: 50, type: 'price'},
|
||||
|
||||
title: 'STC',
|
||||
})
|
||||
|
||||
stcSeries.createPriceLine(buildLine(theme.error, 25, 'low'))
|
||||
stcSeries.createPriceLine(buildLine(theme.info, 75, 'high'))
|
||||
|
||||
const stcData = strategiesValues?.Stc.stc?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.stc,
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
stcSeries.setData(stcData)
|
||||
stcSeries.applyOptions({
|
||||
...baselineOptions,
|
||||
priceFormat: {
|
||||
minMove: 1,
|
||||
precision: 1,
|
||||
type: 'price',
|
||||
},
|
||||
})
|
||||
|
||||
paneCount++
|
||||
}
|
||||
|
||||
if (strategiesValues?.MacdCross != null) {
|
||||
const histogramSeries = chart.current.addHistogramSeries({
|
||||
color: theme.accent,
|
||||
title: 'MACD',
|
||||
pane: paneCount,
|
||||
priceFormat: {
|
||||
precision: 6,
|
||||
type: 'volume',
|
||||
}
|
||||
})
|
||||
|
||||
const macd = strategiesValues.MacdCross.macd?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.histogram,
|
||||
}
|
||||
})
|
||||
|
||||
var priceOptions = {
|
||||
scaleMargins:{
|
||||
top: 0.7,
|
||||
bottom: 0.02,
|
||||
}
|
||||
}
|
||||
histogramSeries.priceScale().applyOptions(priceOptions)
|
||||
// @ts-ignore
|
||||
histogramSeries.setData(macd)
|
||||
|
||||
const macdSeries = chart.current.addLineSeries({
|
||||
color: theme.primary,
|
||||
lineWidth: 1,
|
||||
priceLineVisible: false,
|
||||
priceLineWidth: 1,
|
||||
title: 'EMA',
|
||||
pane: paneCount,
|
||||
priceFormat: {
|
||||
precision: 6,
|
||||
type: 'price',
|
||||
},
|
||||
})
|
||||
|
||||
const macdData = strategiesValues.MacdCross.macd?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.macd,
|
||||
}
|
||||
})
|
||||
|
||||
macdSeries.priceScale().applyOptions(priceOptions)
|
||||
// @ts-ignore
|
||||
macdSeries.setData(macdData)
|
||||
|
||||
const signalSeries = chart.current.addLineSeries({
|
||||
color: theme.info,
|
||||
lineWidth: 1,
|
||||
priceLineVisible: false,
|
||||
priceLineWidth: 1,
|
||||
lineStyle: LineStyle.Dotted,
|
||||
title: 'Signal',
|
||||
pane: paneCount,
|
||||
priceFormat: {
|
||||
precision: 6,
|
||||
type: 'price',
|
||||
},
|
||||
})
|
||||
|
||||
const signalData = strategiesValues.MacdCross.macd?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.signal,
|
||||
}
|
||||
})
|
||||
signalSeries.priceScale().applyOptions(priceOptions)
|
||||
// @ts-ignore
|
||||
signalSeries.setData(signalData)
|
||||
|
||||
paneCount++
|
||||
}
|
||||
|
||||
if (strategiesValues?.StochRsiTrend){
|
||||
const stochRsiSeries = chart.current.addLineSeries({
|
||||
...baselineOptions,
|
||||
priceLineVisible: false,
|
||||
title: 'Stoch RSI',
|
||||
pane: paneCount,
|
||||
})
|
||||
|
||||
const stochRsi = strategiesValues.StochRsiTrend.stochRsi?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.stochRsi,
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
stochRsiSeries.setData(stochRsi)
|
||||
paneCount++
|
||||
}
|
||||
|
||||
if (strategiesValues?.StDev != null) {
|
||||
const stDevSeries = chart.current.addLineSeries({
|
||||
color: theme.primary,
|
||||
lineWidth: 1,
|
||||
priceLineVisible: false,
|
||||
priceLineWidth: 1,
|
||||
title: 'StDev',
|
||||
pane: paneCount,
|
||||
})
|
||||
|
||||
const stDev = strategiesValues.StDev.stdDev?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.stdDev,
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
stDevSeries.setData(stDev)
|
||||
paneCount++
|
||||
|
||||
const zScoreSeries = chart.current.addBaselineSeries({
|
||||
...baselineOptions,
|
||||
baseValue: {price: 0, type: 'price'},
|
||||
title: 'ZScore',
|
||||
pane: paneCount,
|
||||
})
|
||||
|
||||
const zScore = strategiesValues.StDev.stdDev?.map((w) => {
|
||||
return {
|
||||
time: moment(w.date).unix(),
|
||||
value: w.zScore,
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
zScoreSeries.setData(zScore)
|
||||
|
||||
paneCount++
|
||||
}
|
||||
|
||||
if (walletBalances != null) {
|
||||
const walletSeries = chart.current.addBaselineSeries({
|
||||
baseValue: {price: walletBalances[0].value, type: 'price'},
|
||||
bottomFillColor1: 'rgba( 239, 83, 80, 0.05)',
|
||||
bottomFillColor2: 'rgba( 239, 83, 80, 0.28)',
|
||||
bottomLineColor: 'rgba( 239, 83, 80, 1)',
|
||||
pane: 1,
|
||||
topFillColor1: 'rgba( 38, 166, 154, 0.28)',
|
||||
topFillColor2: 'rgba( 38, 166, 154, 0.05)',
|
||||
topLineColor: 'rgba( 38, 166, 154, 1)',
|
||||
pane: paneCount,
|
||||
title: '$',
|
||||
})
|
||||
|
||||
const walletData = walletBalances.map((w) => {
|
||||
@@ -296,12 +569,15 @@ const TradeChart = ({
|
||||
// @ts-ignore
|
||||
walletSeries.setData(walletData)
|
||||
walletSeries.applyOptions({
|
||||
...baselineOptions,
|
||||
priceFormat: {
|
||||
minMove: 0.0001,
|
||||
precision: 4,
|
||||
type: 'price',
|
||||
},
|
||||
|
||||
})
|
||||
paneCount++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1158,6 +1158,54 @@ export class ScenarioClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<FileResponse>(null as any);
|
||||
}
|
||||
|
||||
scenario_UpdateScenario(name: string | null | undefined, loopbackPeriod: number | null | undefined, strategies: string[]): Promise<FileResponse> {
|
||||
let url_ = this.baseUrl + "/Scenario?";
|
||||
if (name !== undefined && name !== null)
|
||||
url_ += "name=" + encodeURIComponent("" + name) + "&";
|
||||
if (loopbackPeriod !== undefined && loopbackPeriod !== null)
|
||||
url_ += "loopbackPeriod=" + encodeURIComponent("" + loopbackPeriod) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(strategies);
|
||||
|
||||
let options_: RequestInit = {
|
||||
body: content_,
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/octet-stream"
|
||||
}
|
||||
};
|
||||
|
||||
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||
return this.http.fetch(url_, transformedOptions_);
|
||||
}).then((_response: Response) => {
|
||||
return this.processScenario_UpdateScenario(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processScenario_UpdateScenario(response: Response): Promise<FileResponse> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200 || status === 206) {
|
||||
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
|
||||
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
|
||||
if (fileName) {
|
||||
fileName = decodeURIComponent(fileName);
|
||||
} else {
|
||||
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||
}
|
||||
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<FileResponse>(null as any);
|
||||
}
|
||||
|
||||
scenario_GetStrategies(): Promise<Strategy[]> {
|
||||
let url_ = this.baseUrl + "/Scenario/strategy";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
@@ -1291,6 +1339,68 @@ export class ScenarioClient extends AuthorizedApiBase {
|
||||
}
|
||||
return Promise.resolve<FileResponse>(null as any);
|
||||
}
|
||||
|
||||
scenario_UpdateStrategy(strategyType: StrategyType | undefined, name: string | null | undefined, period: number | null | undefined, fastPeriods: number | null | undefined, slowPeriods: number | null | undefined, signalPeriods: number | null | undefined, multiplier: number | null | undefined, stochPeriods: number | null | undefined, smoothPeriods: number | null | undefined, cyclePeriods: number | null | undefined): Promise<FileResponse> {
|
||||
let url_ = this.baseUrl + "/Scenario/strategy?";
|
||||
if (strategyType === null)
|
||||
throw new Error("The parameter 'strategyType' cannot be null.");
|
||||
else if (strategyType !== undefined)
|
||||
url_ += "strategyType=" + encodeURIComponent("" + strategyType) + "&";
|
||||
if (name !== undefined && name !== null)
|
||||
url_ += "name=" + encodeURIComponent("" + name) + "&";
|
||||
if (period !== undefined && period !== null)
|
||||
url_ += "period=" + encodeURIComponent("" + period) + "&";
|
||||
if (fastPeriods !== undefined && fastPeriods !== null)
|
||||
url_ += "fastPeriods=" + encodeURIComponent("" + fastPeriods) + "&";
|
||||
if (slowPeriods !== undefined && slowPeriods !== null)
|
||||
url_ += "slowPeriods=" + encodeURIComponent("" + slowPeriods) + "&";
|
||||
if (signalPeriods !== undefined && signalPeriods !== null)
|
||||
url_ += "signalPeriods=" + encodeURIComponent("" + signalPeriods) + "&";
|
||||
if (multiplier !== undefined && multiplier !== null)
|
||||
url_ += "multiplier=" + encodeURIComponent("" + multiplier) + "&";
|
||||
if (stochPeriods !== undefined && stochPeriods !== null)
|
||||
url_ += "stochPeriods=" + encodeURIComponent("" + stochPeriods) + "&";
|
||||
if (smoothPeriods !== undefined && smoothPeriods !== null)
|
||||
url_ += "smoothPeriods=" + encodeURIComponent("" + smoothPeriods) + "&";
|
||||
if (cyclePeriods !== undefined && cyclePeriods !== null)
|
||||
url_ += "cyclePeriods=" + encodeURIComponent("" + cyclePeriods) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Accept": "application/octet-stream"
|
||||
}
|
||||
};
|
||||
|
||||
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||
return this.http.fetch(url_, transformedOptions_);
|
||||
}).then((_response: Response) => {
|
||||
return this.processScenario_UpdateStrategy(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processScenario_UpdateStrategy(response: Response): Promise<FileResponse> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200 || status === 206) {
|
||||
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
|
||||
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
|
||||
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
|
||||
if (fileName) {
|
||||
fileName = decodeURIComponent(fileName);
|
||||
} else {
|
||||
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
|
||||
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
|
||||
}
|
||||
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<FileResponse>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
export class SettingsClient extends AuthorizedApiBase {
|
||||
@@ -1893,6 +2003,7 @@ export interface Backtest {
|
||||
walletBalances: KeyValuePairOfDateTimeAndDecimal[];
|
||||
optimizedMoneyManagement: MoneyManagement;
|
||||
moneyManagement: MoneyManagement;
|
||||
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
|
||||
}
|
||||
|
||||
export enum Ticker {
|
||||
@@ -2152,6 +2263,94 @@ export interface KeyValuePairOfDateTimeAndDecimal {
|
||||
value?: number;
|
||||
}
|
||||
|
||||
export interface StrategiesResultBase {
|
||||
ema?: EmaResult[] | null;
|
||||
macd?: MacdResult[] | null;
|
||||
rsi?: RsiResult[] | null;
|
||||
stoch?: StochResult[] | null;
|
||||
stochRsi?: StochRsiResult[] | null;
|
||||
bollingerBands?: BollingerBandsResult[] | null;
|
||||
chandelierShort?: ChandelierResult[] | null;
|
||||
stc?: StcResult[] | null;
|
||||
stdDev?: StdDevResult[] | null;
|
||||
superTrend?: SuperTrendResult[] | null;
|
||||
chandelierLong?: ChandelierResult[] | null;
|
||||
}
|
||||
|
||||
export interface ResultBase {
|
||||
date?: Date;
|
||||
}
|
||||
|
||||
export interface EmaResult extends ResultBase {
|
||||
ema?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface MacdResult extends ResultBase {
|
||||
macd?: number | null;
|
||||
signal?: number | null;
|
||||
histogram?: number | null;
|
||||
fastEma?: number | null;
|
||||
slowEma?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface RsiResult extends ResultBase {
|
||||
rsi?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
/** Stochastic indicator results includes aliases for those who prefer the simpler K,D,J outputs. See documentation for more information. */
|
||||
export interface StochResult extends ResultBase {
|
||||
oscillator?: number | null;
|
||||
signal?: number | null;
|
||||
percentJ?: number | null;
|
||||
k?: number | null;
|
||||
d?: number | null;
|
||||
j?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface StochRsiResult extends ResultBase {
|
||||
stochRsi?: number | null;
|
||||
signal?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface BollingerBandsResult extends ResultBase {
|
||||
sma?: number | null;
|
||||
upperBand?: number | null;
|
||||
lowerBand?: number | null;
|
||||
percentB?: number | null;
|
||||
zScore?: number | null;
|
||||
width?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface ChandelierResult extends ResultBase {
|
||||
chandelierExit?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface StcResult extends ResultBase {
|
||||
stc?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface StdDevResult extends ResultBase {
|
||||
stdDev?: number | null;
|
||||
mean?: number | null;
|
||||
zScore?: number | null;
|
||||
stdDevSma?: number | null;
|
||||
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
|
||||
}
|
||||
|
||||
export interface SuperTrendResult extends ResultBase {
|
||||
superTrend?: number | null;
|
||||
upperBand?: number | null;
|
||||
lowerBand?: number | null;
|
||||
}
|
||||
|
||||
export interface StartBotRequest {
|
||||
botType: BotType;
|
||||
botName: string;
|
||||
|
||||
@@ -17,6 +17,8 @@ import type {
|
||||
RiskLevel,
|
||||
Scenario,
|
||||
Signal,
|
||||
StrategiesResultBase,
|
||||
StrategyType,
|
||||
Ticker,
|
||||
Timeframe,
|
||||
TradeDirection,
|
||||
@@ -144,6 +146,7 @@ export type IBotRowDetails = {
|
||||
candles: Candle[]
|
||||
positions: Position[]
|
||||
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
|
||||
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
|
||||
}
|
||||
|
||||
export type IBacktestFormInput = {
|
||||
|
||||
@@ -36,17 +36,39 @@ const themes: ThemesInterface = {
|
||||
'--rounded-btn': '0',
|
||||
'--tab-radius': '0',
|
||||
accent: '#343232',
|
||||
'base-100': '#000000',
|
||||
'base-100': '#0B0B0B',
|
||||
'base-200': '#0D0D0D',
|
||||
'base-300': '#1A1919',
|
||||
error: '#ff0000',
|
||||
info: '#0000ff',
|
||||
neutral: '#272626',
|
||||
'base-300': '#0B0B0B',
|
||||
error: '#FF5340',
|
||||
info: '#B0DB43',
|
||||
neutral: '#151515',
|
||||
'neutral-focus': '#343232',
|
||||
primary: '#343232',
|
||||
secondary: '#343232',
|
||||
success: '#008000',
|
||||
warning: '#ffff00',
|
||||
primary: '#54B5F9',
|
||||
secondary: '#a3d9fe',
|
||||
success: '#08C25F',
|
||||
warning: '#EB6F22',
|
||||
},
|
||||
'[data-theme=kaigen]': {
|
||||
'--animation-btn': '0',
|
||||
'--animation-input': '0',
|
||||
'--btn-focus-scale': '1',
|
||||
'--btn-text-case': 'lowercase',
|
||||
'--rounded-badge': '0',
|
||||
'--rounded-box': '0',
|
||||
'--rounded-btn': '0',
|
||||
'--tab-radius': '0',
|
||||
accent: '#343232',
|
||||
'base-100': '#0B0B0B',
|
||||
'base-200': '#0D0D0D',
|
||||
'base-300': '#0B0B0B',
|
||||
error: '#FF5340',
|
||||
info: '#B0DB43',
|
||||
neutral: '#151515',
|
||||
'neutral-focus': '#343232',
|
||||
primary: '#54B5F9',
|
||||
secondary: '#a3d9fe',
|
||||
success: '#08C25F',
|
||||
warning: '#EB6F22',
|
||||
},
|
||||
|
||||
'[data-theme=coffee]': {
|
||||
@@ -61,18 +83,18 @@ const themes: ThemesInterface = {
|
||||
success: '#9DB787',
|
||||
warning: '#FFD25F',
|
||||
},
|
||||
'[data-theme=cyberpunk]': {
|
||||
'--rounded-badge': '0',
|
||||
'--rounded-box': '0',
|
||||
'--rounded-btn': '0',
|
||||
'--tab-radius': '0',
|
||||
accent: '#c07eec',
|
||||
'base-100': '#ffee00',
|
||||
neutral: '#423f00',
|
||||
'neutral-content': '#ffee00',
|
||||
primary: '#ff7598',
|
||||
secondary: '#75d1f0',
|
||||
},
|
||||
// '[data-theme=cyberpunk]': {
|
||||
// '--rounded-badge': '0',
|
||||
// '--rounded-box': '0',
|
||||
// '--rounded-btn': '0',
|
||||
// '--tab-radius': '0',
|
||||
// accent: '#c07eec',
|
||||
// 'base-100': '#ffee00',
|
||||
// neutral: '#423f00',
|
||||
// 'neutral-content': '#ffee00',
|
||||
// primary: '#ff7598',
|
||||
// secondary: '#75d1f0',
|
||||
// },
|
||||
'[data-theme=lofi]': {
|
||||
'--animation-btn': '0',
|
||||
'--animation-input': '0',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
daisyui: {
|
||||
themes: true,
|
||||
themes: ['black', 'coffee', 'cyberpunk', 'lofi', 'retro', 'kaigen'],
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography'), require('daisyui')],
|
||||
theme: {
|
||||
|
||||
Reference in New Issue
Block a user