/* * Copyright Reiryoku Technologies and its contributors, www.reiryoku.com, www.mida.org * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ import { MidaTradingAccount, } from "#accounts/MidaTradingAccount"; import { MidaMarketComponent, } from "#components/MidaMarketComponent"; import { MidaMarketComponentConstructor, } from "#components/MidaMarketComponentConstructor"; import { MidaMarketComponentDependencyDeclaration, } from "#components/MidaMarketComponentDependencyDeclaration"; import { MidaMarketComponentIndicatorDeclaration, } from "#components/MidaMarketComponentIndicatorDeclaration"; import { MidaMarketComponentOracle, } from "#components/MidaMarketComponentOracle"; import { MidaMarketComponentParameterDeclaration, } from "#components/MidaMarketComponentParameterDeclaration"; import { MidaMarketComponentState, } from "#components/MidaMarketComponentState"; import { MidaIndicator, } from "#indicators/MidaIndicator"; import { logger, } from "#loggers/MidaLogger"; import { MidaSymbol, } from "#symbols/MidaSymbol"; import { MidaTimeframe, } from "#timeframes/MidaTimeframe"; export type MidaMarketComponentMakerParameters = { component: MidaMarketComponent; params?: Record; tradingAccount: MidaTradingAccount; symbol: string; }; // eslint-disable-next-line max-lines-per-function, max-len export const makeComponent = async (parameters: MidaMarketComponentMakerParameters): Promise => { const state: MidaMarketComponentState = await makeComponentState(parameters); const oracle: MidaMarketComponentOracle = new MidaMarketComponentOracle(state); await oracle.start(); return state; }; // eslint-disable-next-line max-lines-per-function, max-len, complexity export const makeComponentState = async (parameters: MidaMarketComponentMakerParameters): Promise => { const { component, params, tradingAccount, symbol, } = parameters; // const completeSymbol: MidaSymbol | undefined = await tradingAccount.getSymbol(symbol); if (!completeSymbol) { logger.fatal(`Symbol ${symbol} not supported`); throw new Error(); } // const [ bid, ask, ] = await Promise.all([ tradingAccount.getSymbolBid(symbol), tradingAccount.getSymbolAsk(symbol), ]); let state: MidaMarketComponentState = { $component: component, $dependencies: [], $tradingAccount: tradingAccount, $watcher: { watchTicks: true, watchPeriods: false, }, $symbol: symbol, $completeSymbol: completeSymbol, $bid: bid, $ask: ask, $ticks: [], $periods: {}, $livePeriods: {}, $indicators: {}, }; await component.beforeCreate?.call(state); state = { ...component.state?.call(state), ...state, }; // const declaredParams: Record = component.params ?? {}; for (const propertyName of Object.keys(params ?? {})) { if (!declaredParams[propertyName]) { logger.warn(`Market Component | Undeclared param ${propertyName} will be ignored`); } } for (const propertyName of Object.keys(declaredParams)) { const { type, required, default: defaultValue, } = declaredParams[propertyName]; const value: any = params?.[propertyName] ?? (typeof defaultValue === "function" ? defaultValue() : defaultValue); if (!value && required) { logger.fatal(`Market Component | Missing required param ${propertyName}`); throw new Error(); } if (type && value?.constructor !== type) { logger.warn(`Market Component | Value of param ${propertyName} doesn't match its declared type`); } Object.defineProperty(state, propertyName, { get (): any { return value; }, }); } // // state.$watcher = { ...state.$watcher, ...component.watcher?.call(state), }; // // const computed = component.computed ?? {}; for (const propertyName of Object.keys(computed)) { Object.defineProperty(state, propertyName, { get (): any { return computed?.[propertyName]?.call(state); }, }); } // // const methods = component.methods ?? {}; for (const propertyName of Object.keys(methods)) { Object.defineProperty(state, propertyName, { value: methods?.[propertyName].bind(state), writable: true, }); } // // const indicators: Record = typeof component.indicators === "function" ? component.indicators.call(state) : component.indicators ?? {}; for (const propertyName of Object.keys(indicators)) { const { type, params, input, } = indicators?.[propertyName]; const timeframe: MidaTimeframe = input?.timeframe as MidaTimeframe; if (!MidaIndicator.has(type)) { logger.fatal(`Market Component | Indicator ${type} not found, have you installed its plugin?`); throw new Error(); } const indicator: MidaIndicator = MidaIndicator.create(type, params); state.$watcher.watchPeriods = true; state.$watcher.timeframes = [ ...new Set([ ...state.$watcher.timeframes ?? [], timeframe, ]), ]; state.$indicators[propertyName] = { indicator, input: { type: "close", live: false, limit: 2000, ...input, }, }; Object.defineProperty(state, propertyName, { get (): MidaIndicator { return indicator; }, }); } // // const dependencies: Record = typeof component.dependencies === "function" ? component.dependencies.call(state) : component.dependencies ?? {}; for (const propertyName of Object.keys(dependencies)) { const declaration: MidaMarketComponentDependencyDeclaration = dependencies[propertyName]; const constructor: MidaMarketComponentConstructor = typeof declaration === "object" ? declaration.type : declaration; const dependencyComponent: MidaMarketComponent = constructor.$component; const dependencyParams: Record | undefined = typeof declaration === "object" ? declaration.params : undefined; const dependencyState: MidaMarketComponentState = await makeComponentState({ component: dependencyComponent, params: dependencyParams, tradingAccount, symbol, }); state.$dependencies.push(dependencyState); Object.defineProperty(state, propertyName, { get (): MidaMarketComponentState { return dependencyState; }, }); } // await component.created?.call(state); return state; };