/* * Copyright (c) 2004-2011 Marco Maccaferri and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marco Maccaferri - initial API and implementation */ package org.eclipsetrader.core.ats.engines; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipsetrader.core.IScript; import org.eclipsetrader.core.ats.IScriptStrategy; import org.eclipsetrader.core.feed.Bar; import org.eclipsetrader.core.feed.IBar; import org.eclipsetrader.core.feed.IBarOpen; import org.eclipsetrader.core.feed.IHistory; import org.eclipsetrader.core.feed.IOHLC; import org.eclipsetrader.core.feed.IQuote; import org.eclipsetrader.core.feed.ITrade; import org.eclipsetrader.core.feed.TimeSpan; import org.eclipsetrader.core.instruments.ISecurity; import org.eclipsetrader.core.internal.CoreActivator; import org.eclipsetrader.core.repositories.IRepositoryService; import org.eclipsetrader.core.trading.IPosition; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; public class JavaScriptEngineInstrument { public static final String FUNCTION_ON_STRATEGY_START = "onStrategyStart"; //$NON-NLS-1$ public static final String FUNCTION_ON_QUOTE = "onQuote"; //$NON-NLS-1$ public static final String FUNCTION_ON_TRADE = "onTrade"; //$NON-NLS-1$ public static final String FUNCTION_ON_BAR_OPEN = "onBarOpen"; //$NON-NLS-1$ public static final String FUNCTION_ON_BAR = "onBar"; //$NON-NLS-1$ public static final String FUNCTION_ON_POSITION_OPENED = "onPositionOpened"; //$NON-NLS-1$ public static final String FUNCTION_ON_POSITION_CHANGED = "onPositionChanged"; //$NON-NLS-1$ public static final String FUNCTION_ON_POSITION_CLOSED = "onPositionClosed"; //$NON-NLS-1$ public static final String PROPERTY_QUOTE = "quote"; //$NON-NLS-1$ public static final String PROPERTY_TRADE = "trade"; //$NON-NLS-1$ public static final String PROPERTY_BAR = "bar"; //$NON-NLS-1$ public static final String PROPERTY_BARS = "bars"; //$NON-NLS-1$ public static final String PROPERTY_POSITION = "position"; //$NON-NLS-1$ private final Scriptable scope; private final ISecurity instrument; private final IScriptStrategy strategy; private Function onQuote; private Function onTrade; private Function onBarOpen; private Function onBar; private Function onPositionOpened; private Function onPositionChanged; private Function onPositionClosed; private BarsDataSeriesFunction bars; private final Log log = LogFactory.getLog(getClass()); public JavaScriptEngineInstrument(Scriptable sharedScope, ISecurity instrument, IScriptStrategy strategy) throws Exception { this.instrument = instrument; this.strategy = strategy; Context cx = Context.enter(); try { scope = cx.newObject(sharedScope); scope.setPrototype(sharedScope); for (Object id : ScriptableObject.getPropertyIds(sharedScope)) { Object obj = ScriptableObject.getProperty(sharedScope, (String) id); ScriptableObject.putProperty(scope, (String) id, obj); } scope.setParentScope(null); ScriptableObject.defineClass(scope, BarsDataSeriesFunction.class); ScriptableObject.defineClass(scope, LimitOrderFunction.class); ScriptableObject.defineClass(scope, MarketOrderFunction.class); ScriptableObject.defineClass(scope, HasPositionFunction.class); defineClasses(); ScriptableObject.putProperty(scope, BaseOrderFunction.PROPERTY_INSTRUMENT, Context.javaToJS(instrument, scope)); bars = (BarsDataSeriesFunction) cx.newObject(scope, BarsDataSeriesFunction.FUNCTION_NAME); ScriptableObject.putProperty(scope, PROPERTY_BARS, bars); cx.evaluateString(scope, "importPackage(org.eclipsetrader.core.feed);", strategy.getName(), 0, null); //$NON-NLS-1$ cx.evaluateString(scope, "importPackage(org.eclipsetrader.core.instruments);", strategy.getName(), 0, null); //$NON-NLS-1$ cx.evaluateString(scope, "importPackage(org.eclipsetrader.core.trading);", strategy.getName(), 0, null); //$NON-NLS-1$ for (IScript script : strategy.getIncludes()) { cx.evaluateString(scope, script.getText(), script.getName(), 1, null); } cx.evaluateString(scope, strategy.getText(), strategy.getName(), 1, null); Object obj = scope.get(FUNCTION_ON_QUOTE, scope); if (obj instanceof Function) { onQuote = (Function) obj; } obj = scope.get(FUNCTION_ON_TRADE, scope); if (obj instanceof Function) { onTrade = (Function) obj; } obj = scope.get(FUNCTION_ON_BAR_OPEN, scope); if (obj instanceof Function) { onBarOpen = (Function) obj; } obj = scope.get(FUNCTION_ON_BAR, scope); if (obj instanceof Function) { onBar = (Function) obj; } obj = scope.get(FUNCTION_ON_POSITION_OPENED, scope); if (obj instanceof Function) { onPositionOpened = (Function) obj; } obj = scope.get(FUNCTION_ON_POSITION_CHANGED, scope); if (obj instanceof Function) { onPositionChanged = (Function) obj; } obj = scope.get(FUNCTION_ON_POSITION_CLOSED, scope); if (obj instanceof Function) { onPositionClosed = (Function) obj; } } finally { Context.exit(); } } protected void defineClasses() { if (!Platform.isRunning() || Platform.getExtensionRegistry() == null) { return; } IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(CoreActivator.SCRIPTS_EXTENSION_ID); if (extensionPoint != null) { IConfigurationElement[] configElements = extensionPoint.getConfigurationElements(); for (int j = 0; j < configElements.length; j++) { String strID = configElements[j].getAttribute("class"); //$NON-NLS-1$ try { ScriptableObject object = (ScriptableObject) configElements[j].createExecutableExtension("class"); //$NON-NLS-1$ ScriptableObject.defineClass(scope, object.getClass()); } catch (Exception e) { Status status = new Status(IStatus.WARNING, CoreActivator.PLUGIN_ID, 0, "Unable to define function " + strID, e); //$NON-NLS-1$ CoreActivator.log(status); } } } } public ISecurity getInstrument() { return instrument; } public void onStrategyStart() { Context cx = Context.enter(); try { Object obj = scope.get(FUNCTION_ON_STRATEGY_START, scope); if (obj instanceof Function) { ((Function) obj).call(cx, scope, scope, new Object[0]); } } finally { Context.exit(); } } public void onQuote(IQuote quote) { Context cx = Context.enter(); try { if (onQuote != null) { onQuote.call(cx, scope, scope, new Object[] { quote }); } ScriptableObject.putProperty(scope, PROPERTY_QUOTE, quote); } finally { Context.exit(); } } public void onTrade(ITrade trade) { Context cx = Context.enter(); try { if (onTrade != null) { onTrade.call(cx, scope, scope, new Object[] { trade }); } ScriptableObject.putProperty(scope, PROPERTY_TRADE, trade); } finally { Context.exit(); } } public void onBarOpen(IBarOpen bar) { Context cx = Context.enter(); try { if (onBarOpen != null) { onBarOpen.call(cx, scope, scope, new Object[] { bar }); } } finally { Context.exit(); } } public void onBar(IBar bar) { Context cx = Context.enter(); try { bars.append(bar); if (onBar != null) { onBar.call(cx, scope, scope, new Object[] { bar }); } ScriptableObject.putProperty(scope, PROPERTY_BAR, bar); } finally { Context.exit(); } } public void onPositionOpen(IPosition position) { log.info("onPositionOpen: " + position); Context cx = Context.enter(); try { ScriptableObject.putProperty(scope, PROPERTY_POSITION, position); if (onPositionOpened != null) { onPositionOpened.call(cx, scope, scope, new Object[] { position }); } } finally { Context.exit(); } } public void onPositionChange(IPosition position) { log.info("onPositionChange: " + position); Context cx = Context.enter(); try { ScriptableObject.putProperty(scope, PROPERTY_POSITION, position); if (onPositionChanged != null) { onPositionChanged.call(cx, scope, scope, new Object[] { position }); } } finally { Context.exit(); } } public void onPositionClosed(IPosition position) { log.info("onPositionClosed: " + position); Context cx = Context.enter(); try { ScriptableObject.deleteProperty(scope, PROPERTY_POSITION); if (onPositionClosed != null) { onPositionClosed.call(cx, scope, scope, new Object[] { position }); } } finally { Context.exit(); } } public void setPosition(IPosition position) { Context.enter(); try { ScriptableObject.putProperty(scope, PROPERTY_POSITION, position); } finally { Context.exit(); } } public Object get(String name) { return scope.get(name, scope); } public Scriptable getScope() { return scope; } public IBar[] getBars() { return bars.toArray(); } public void backfill(int backfillBars) { BundleContext context = CoreActivator.getDefault().getBundle().getBundleContext(); ServiceReference<IRepositoryService> serviceReference = context.getServiceReference(IRepositoryService.class); if (serviceReference != null) { IRepositoryService repositoryService = context.getService(serviceReference); IHistory history = repositoryService.getHistoryFor(instrument); IOHLC[] ohlc = history.getOHLC(); TimeSpan[] timeSpan = strategy.getBarsTimeSpan(); for (int i = 0; i < timeSpan.length; i++) { if (timeSpan[i].equals(TimeSpan.days(1))) { for (int index = ohlc.length - backfillBars; index < ohlc.length; index++) { bars.append(new Bar(ohlc[index].getDate(), timeSpan[i], ohlc[index].getOpen(), ohlc[index].getHigh(), ohlc[index].getLow(), ohlc[index].getClose(), ohlc[index].getVolume())); } } else { int filled = 0; for (int index = ohlc.length - 1; index >= 0 && filled < backfillBars; index--) { IHistory subHistory = history.getSubset(ohlc[index].getDate(), ohlc[index].getDate(), timeSpan[i]); IOHLC[] subOhlc = subHistory.getOHLC(); for (int ii = subOhlc.length - 1; ii >= 0 && filled < backfillBars; ii--) { bars.prepend(new Bar(subOhlc[ii].getDate(), timeSpan[i], subOhlc[ii].getOpen(), subOhlc[ii].getHigh(), subOhlc[ii].getLow(), subOhlc[ii].getClose(), subOhlc[ii].getVolume())); filled++; } } } } context.ungetService(serviceReference); } } }