commit 55c11b374a70f03ab3d3fa213e2e0af3443bb934 Author: newellp88 Date: Tue May 8 20:51:20 2018 +0800 first commit diff --git a/backend/__pycache__/account.cpython-35.pyc b/backend/__pycache__/account.cpython-35.pyc new file mode 100644 index 0000000..6002c3f Binary files /dev/null and b/backend/__pycache__/account.cpython-35.pyc differ diff --git a/backend/__pycache__/config.cpython-35.pyc b/backend/__pycache__/config.cpython-35.pyc new file mode 100644 index 0000000..b524be8 Binary files /dev/null and b/backend/__pycache__/config.cpython-35.pyc differ diff --git a/backend/__pycache__/indicators.cpython-35.pyc b/backend/__pycache__/indicators.cpython-35.pyc new file mode 100644 index 0000000..f9cf2f2 Binary files /dev/null and b/backend/__pycache__/indicators.cpython-35.pyc differ diff --git a/backend/__pycache__/orders.cpython-35.pyc b/backend/__pycache__/orders.cpython-35.pyc new file mode 100644 index 0000000..b2fb1ee Binary files /dev/null and b/backend/__pycache__/orders.cpython-35.pyc differ diff --git a/backend/__pycache__/pricing.cpython-35.pyc b/backend/__pycache__/pricing.cpython-35.pyc new file mode 100644 index 0000000..80ebf5f Binary files /dev/null and b/backend/__pycache__/pricing.cpython-35.pyc differ diff --git a/backend/account.py b/backend/account.py new file mode 100644 index 0000000..ecda838 --- /dev/null +++ b/backend/account.py @@ -0,0 +1,25 @@ +# account details for export + +from oandapyV20 import API +from oandapyV20.endpoints.accounts import AccountSummary +from .config import accountID, token + +api = API(token) + +r = AccountSummary(accountID) +summary = api.request(r) # base dict +account = summary['account'] # item containing dict of account data + +account_summary = dict() + +balance = account['balance'] +open_trades = account['openTradeCount'] +open_positions = account['openPositionCount'] +pnl = account['pl'] +carrying_costs = account['financing'] +open_pnl = account['unrealizedPL'] +nav = account['NAV'] +margin_used = account['marginUsed'] +margin_available = account['marginAvailable'] +margin_call_pct = account['marginCallPercent'] +margin_rate = account['marginRate'] diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 0000000..006361f --- /dev/null +++ b/backend/config.py @@ -0,0 +1,20 @@ +# contains settings and common lists of assets to trade + +# basic account/environment details +accountID = '101-011-4913967-001' +token = '88899e549cd4edf80ddcf7e4f8a57f60-f7067b6e2e3c2ef5ba5de3116e7ae5e2' +env = 'practice' # change this to 'live' when you're ready + +# major portfolios + +currencies = ['EUR_USD', 'GBP_USD', 'USD_JPY', 'AUD_USD', 'NZD_USD', + 'USD_CHF', 'USD_CAD', 'XAU_USD', 'AUD_JPY', 'EUR_JPY', + 'GBP_JPY'] + +indicies = ['SPX500_USD', 'AU200_AUD', 'CN50_USD', 'EU50_EUR', 'FR40_EUR', + 'DE30_EUR', 'HK33_HKD', 'IN50_USD', 'JP225_USD', 'NL25_EUR', + 'SG30_SGD', 'TWIX_USD', 'UK100_GBP', 'US30_USD', 'NAS100_USD', + 'US2000_USD'] + +commodities = ['BCO_USD', 'XCU_USD', 'CORN_USD', 'NATGAS_USD', 'SOYBN_USD', + 'SUGAR_USD', 'WTICO_USD', 'WHEAT_USD'] diff --git a/backend/indicators.py b/backend/indicators.py new file mode 100644 index 0000000..ba7defb --- /dev/null +++ b/backend/indicators.py @@ -0,0 +1,112 @@ +# short list of trading indicators +import numpy as np +import pandas as pd +from sklearn import ensemble, tree +from sklearn.model_selection import train_test_split + +def sma(series, window): + mavg = series.rolling(window=window, min_periods=window).mean() + return mavg + +# exponential weighted moving average +def ewma(series, span): + ema = pd.ewma(series, span=span) + return ema + +def pivotPoints(df): + n = len(df) - 1 # pivotPoints would be based on the last candle + pp = (df['High'] + df['Low'] + df['Close']) / 3 + r1 = (2 * pp) - df['Low'] + s1 = (2 * pp) - df['High'] + r2 = (pp - s1) + r1 + s2 = pp - (r1 - s1) + r3 = (pp - s2) + r2 + s3 = pp - (r2 - s2) + + pivots = {'PP': pp, 'R1': r1, 'R2': r2, 'R3': r3, + 'S1': s1, 'S2': s2, 'S3': s3} + + return pivots + +# rate of change, applied directly to the dataframe +def roc(df): + closes = df['Close'].apply(float) + df['ROC'] = closes.diff() * 100 + df['ROC'].fillna(0) + return df + +# stochastic oscillator, applied directly to the dataframe +def stoch(df, period_K, period_D, graph=False): + df['Low'] = pd.to_numeric(df['Low'], errors='coerce') + df['Lstoch'] = df['Low'].rolling(window=period_K).min() + df['High'] = pd.to_numeric(df['High'], errors='coerce') + df['Hstoch'] = df['High'].rolling(window=period_K).max() + df['Close'] = pd.to_numeric(df['Close'], errors='coerce') + df['%K'] = 100*((df['Close'] - df['Lstoch']) / (df['Hstoch'] - df['Lstoch'])) + df['%D'] = df['%K'].rolling(window=period_D).mean() + + if graph == True: + fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(20,10)) + df['Close'].plot(ax=axes[0])#, title='Close' + df[['K', 'D']].plot(ax=axes[1])#, title='Oscillator' + plt.show() + + return df + +# bollinger bands, applied directly to the dataframe +def bollBands(df, window, n_std, graph=False): + close = df['Close'] + rolling_mean = close.rolling(window).mean() + rolling_std = close.rolling(window).std() + + df['rolling_mean'] = rolling_mean + df['boll_high'] = rolling_mean + (rolling_std * n_std) + df['boll_low'] = rolling_mean - (rolling_std * n_std) + + if graph == True: + plt.plot(close) + plt.plot(df['Rolling Mean']) + plt.plot(df['Bollinger High']) + plt.plot(df['Bollinger Low']) + plt.show() + + return df + +# AdaBoostRegressor with Decision Tree base, uses most of the standard df +def AdaBoost(df): + # clean the data + n = len(df) + X = np.asarray(df[['Open','High','Low','Volume']][:n-1]) # add or subtract input data columns here + X = X.reshape(n-1, 4) # adjust '4' to match the number of input columns used above + y = np.asarray(df[['Close']][1:]) + # build and score the model + split = 0.8 + X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=split) + dtree = tree.DecisionTreeRegressor(max_depth=1000) + model = ensemble.AdaBoostRegressor(n_estimators=5000, learning_rate=2.0, base_estimator=dtree) + model.fit(X_train, y_train) + score = model.score(X_test, y_test) + #print("Model1 accuracy: ", score) + # make a prediction + prediction = model.predict(df[['Open','High','Low','Volume']][n-1:]) + #print("AdaBoost predicted close for next candle: ", prediction) + return prediction + +# RandomForestRegressor with Decision Tree base, uses most of the standard df +def RandomForest(df): + # clean the data + n = len(df) + X = np.asarray(df[['Open','High','Low','Volume']][:n-1]) # add or subtract input data columns here + X = X.reshape(n-1, 4) # adjust '4' to match the number of input columns used above + y = np.asarray(df[['Close']][1:]) + # build and score the model + split = 0.8 + X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=split) + model = ensemble.RandomForestRegressor(n_estimators=10000, max_depth=1000) + model.fit(X_train, y_train) + score = model.score(X_test, y_test) + #print("Model2 accuracy: ", score) + # make a prediction + prediction = model.predict(df[['Open','High','Low','Volume']][n-1:]) + #print("RandomForest predicted close for next candle: ", prediction) + return prediction diff --git a/backend/orders.py b/backend/orders.py new file mode 100644 index 0000000..fe97dd3 --- /dev/null +++ b/backend/orders.py @@ -0,0 +1,77 @@ +# class to support opening and closing trades +import json +from .config import accountID, token +from oandapyV20 import API +import oandapyV20.endpoints.trades as trades +import oandapyV20.endpoints.orders as orders +from oandapyV20.contrib.requests import TradeCloseRequest +from oandapyV20.exceptions import V20Error +from .account import margin_available, margin_rate +from oandapyV20.contrib.requests import (MarketOrderRequest, + TakeProfitDetails, + StopLossDetails, + TrailingStopLossOrderRequest) + +api = API(token) + +def weight(price, allocation): + trade_margin = float(margin_available) * allocation + trade_value = trade_margin / float(margin_rate) + w = round((trade_value/float(price)), 0) + return w + +def order(instrument, weight, price, TP, SL): + + if weight > 0: + mktOrder = MarketOrderRequest( + instrument=instrument, + units=weight, + takeProfitOnFill=TakeProfitDetails(price=TP).data, + stopLossOnFill=StopLossDetails(price=SL).data) + r = orders.OrderCreate(accountID, data=mktOrder.data) + + try: + rv = api.request(r) + #print(r.status_code) + except V20Error as e: + print(r.status_code, e) + else: + #print(json.dumps(rv, indent=4)) + pass + + else: + mktOrder = MarketOrderRequest( + instrument=instrument, + units=weight, + takeProfitOnFill=TakeProfitDetails(price=TP).data, + stopLossOnFill=StopLossDetails(price=SL).data) + r = orders.OrderCreate(accountID, data=mktOrder.data) + + try: + rv = api.request(r) + #print(r.status_code) + except V20Error as e: + print(r.status_code, e) + else: + #print(json.dumps(rv, indent=4)) + pass + + return r.response + +def trailingStop(tradeID, distance=0.1): + activeStop = TrailingStopLossOrderRequest(tradeID=tradeID, + r = orders.OrderCreate(accountID, + data=activeStop.data)) + api.request(r) + return r.response + +def close(tradeID): + close_trade = TradeCloseRequest() + r = trades.TradeClose(accountID, tradeID, data=close_trade.data) + api.request(r) + return r.response + +def closeAll(tradeID_list): + for trade in tradeID_list: + close(trade) + print(trade, "Closed") diff --git a/backend/pricing.py b/backend/pricing.py new file mode 100644 index 0000000..99a99da --- /dev/null +++ b/backend/pricing.py @@ -0,0 +1,57 @@ +# price history or stream functions + +from .config import token, accountID, env +from oandapyV20 import API +from oandapyV20.endpoints.instruments import InstrumentsCandles +from oandapyV20.endpoints.pricing import PricingStream +import pandas as pd +import time + +api = API(token) + +# returns the last 500 OHLCV candles for an instrument +# maximum count is 500 +# window: M1, M5, M15, H, H4, D, etc. +def history(instrument, window, collection=False): + instrument = instrument + data = list() + client = API(token) + params = {"count": 500, "granularity": window} + r = InstrumentsCandles(instrument, params) + client.request(r) + resp = r.response + for candle in resp.get('candles'): + dt = candle['time'] + Open = candle['mid']['o'] + High = candle['mid']['h'] + Low = candle['mid']['l'] + Close = candle['mid']['c'] + Volume = candle['volume'] + update = [dt, Open, High, Low, Close, Volume] + data.append(update) + df = pd.DataFrame(data, columns=['dt','Open','High','Low','Close','Volume']) + # collect data, useful for research and weekends/holidays when the market isn't open + if collection == True: + title = 'data/%s_%s_history.csv' % (instrument, window) + df.to_csv(title) + + return df + +# creates a pricing stream, +def stream(instrument, window): + request_params = {"timeout":100} + params = {"instruments":instrument, "granularity":window} + api = API(access_token=token,environment=env, request_params=request_params) + r = PricingStream(accountID=accountID, params=params) + + while True: + try: + api.request(r) + for R in r.response: + time = R['time'] + ask = R['asks'][0]['price'] + bid = R['bids'][0]['price'] + return time, ask, bid + except Exception as e: + print(e) + continue diff --git a/db_collection.py b/db_collection.py new file mode 100644 index 0000000..17dc4de --- /dev/null +++ b/db_collection.py @@ -0,0 +1,51 @@ +""" +script that runs continuously and collects pricing and technical indicators data, +entering the data into instrument/window data tables. +""" +import sqlite3 +import sqlalchemy +import pandas as pd +from datetime import datetime +from backend.pricing import history +from backend.config import currencies +from backend.indicators import sma, stoch, pivotPoints, bollBands + + +windows = ['M5', 'M15', 'H1', 'H4', 'D'] +for instr in currencies: + for w in windows: + LiteCurrencyDB(instr, w) + +def LiteCurrencyDB(instr, w): + # connect to the DB + db = sqlite3.connect('liteDB/currencies.db') + c = db.cursor() + table_name = "%s_%s" % (instr, w) + # get last 500 data points + df = history(instr, w) + prices = df['Close'] + # set the dataframe with our technical indicators + df['fast_sma'] = sma(prices, 6) + df['slow_sma'] = sma(prices, 10) + df = stoch(df, 6, 3) + df = bollBands(df, 5, 0.5) + pp = pd.DataFrame(data=pivotPoints(df)) + df = pd.concat([df,pp], axis=1, join_axes=[df.index]) + inputs = list(tuple(row for idx, row in df.iterrows())) + # insert them into the dedicated table for this currency and timeframe + c.execute('''CREATE TABLE IF NOT EXISTS {}( + dt TEXT, Open REAL, High REAL, Low REAL, Close REAL, + Volume INTEGER, fast_sma REAL, slow_sma REAL, Lstoch REAL, + Hstoch REAL, K REAL, D REAL, rolling_mean REAL, boll_high REAL, + boll_low REAL, PP REAL, R1 REAL, R2 REAL, R3 REAL, S1 REAL, + S2 REAL, S3 REAL + )'''.format(table_name)) + c.executemany('''INSERT INTO {}( + dt, Open, High, Low, Close, Volume, fast_sma, slow_sma, Lstoch, + Hstoch, K, D, rolling_mean, boll_high, boll_low, PP, R1, R2, R3, + S1, S2, S3 + ) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''' + .format(table_name), inputs) + db.commit() + print('table created successfully') + return db, df diff --git a/liteDB/currencies.db b/liteDB/currencies.db new file mode 100644 index 0000000..5cf0fef Binary files /dev/null and b/liteDB/currencies.db differ