Skip to content

Commit

Permalink
Merge pull request CryptoSignal#105 from jrbartola/fix-decision
Browse files Browse the repository at this point in the history
Fixed Backtesting Decision Buy (Issue # 102)
  • Loading branch information
jrbartola authored Jan 23, 2018
2 parents a3fecb7 + d63d4c3 commit 511b6a5
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 22 deletions.
11 changes: 7 additions & 4 deletions app/behaviours/ui/backtesting/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def get_points(self):
Returns the indicators specified in the **kwargs dictionary as a json-serializable dictionary
'''
def get_indicators(self, **kwargs):
import numpy as np
from math import isnan

# Indicators are hardcoded for now. Will be updated to accommodate variable-sized MA's
response = {
Expand All @@ -55,8 +55,9 @@ def get_indicators(self, **kwargs):
period = kwargs["bollinger"]
assert type(period) is int

bbupper = [np.nan_to_num(datum["values"][0]) for datum in self.indicators.analyze_bollinger_bands(closings, all_data=True)]
bblower = [np.nan_to_num(datum["values"][2]) for datum in self.indicators.analyze_bollinger_bands(closings, all_data=True)]
# Offset each band by "period" data points
bbupper = [(i, datum["values"][0]) for i, datum in enumerate(self.indicators.analyze_bollinger_bands(closings, all_data=True))][period:]
bblower = [(i, datum["values"][2]) for i, datum in enumerate(self.indicators.analyze_bollinger_bands(closings, all_data=True))][period:]

response['bollinger_upper'] = bbupper
response['bollinger_lower'] = bblower
Expand All @@ -68,7 +69,9 @@ def get_indicators(self, **kwargs):
assert type(periods) is list

for period in periods:
response['sma' + str(period)] = [np.nan_to_num(datum["values"][0]) for datum in self.indicators.analyze_sma(closings, period_count=period, all_data=True)]
# Offset each sma by "period" data points
response['sma' + str(period)] = [(i, datum["values"][0]) for i, datum in
enumerate(self.indicators.analyze_sma(closings, period_count=period, all_data=True))][period:]

return response

Expand Down
29 changes: 20 additions & 9 deletions app/behaviours/ui/backtesting/decision.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,28 @@ def __init__(self, indicators):
:returns True iff each indicator satisfies a comparision using it's 'comparator' value with its 'value' value. False otherwise
'''
def should_buy(self, buy_strategy):
from math import isnan

for indicator, body in buy_strategy.items():
comparator, value = body['comparator'], body['value']

if not isinstance(value, (int, float)):
value = self.indicators[value]
value = self.indicators[value][0]

if isnan(value):
# If the indicator is NaN (possibly due to insufficient data), return False
return False

if comparator == 'LT':
if self.indicators[indicator] >= value[0]:
if self.indicators[indicator] >= value:
return False

elif comparator == 'EQ':
if self.indicators[indicator] != value[0]:
if self.indicators[indicator] != value:
return False

elif comparator == 'GT':
if self.indicators[indicator] <= value[0]:
if self.indicators[indicator] <= value:
return False

return True
Expand All @@ -57,23 +63,28 @@ def should_buy(self, buy_strategy):
:returns True iff each indicator satisfies a comparision using it's 'comparator' value with its 'value' value. False otherwise
'''
def should_sell(self, sell_strategy):
for indicator, body in sell_strategy.items():
from math import isnan

for indicator, body in sell_strategy.items():
comparator, value = body['comparator'], body['value']

if not isinstance(value, (int, float)):
value = self.indicators[value]
value = self.indicators[value][0]

if isnan(value):
# If the indicator is NaN (possibly due to insufficient data), return False
return False

if comparator == 'LT':
if self.indicators[indicator] >= value[0]:
if self.indicators[indicator] >= value:
return False

elif comparator == 'EQ':
if self.indicators[indicator] != value[0]:
if self.indicators[indicator] != value:
return False

elif comparator == 'GT':
if self.indicators[indicator] <= value[0]:
if self.indicators[indicator] <= value:
return False

return True
12 changes: 4 additions & 8 deletions app/behaviours/ui/www/react/Plot.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ class Plot extends React.Component {
.x(d => x(d[0]))
.y(d => y(d[1]));

const indicator = d3.line()
.x((d, i) => x(i))
.y(d => y(d));

x.domain(d3.extent(this.props.closingPrices, d => d[0] ));
y.domain(d3.extent(this.props.closingPrices, d => d[1] ));

Expand Down Expand Up @@ -113,7 +109,7 @@ class Plot extends React.Component {
.attr("stroke-linecap", "round")
.attr("stroke-dasharray", "5, 5")
.attr("stroke-width", 1.5)
.attr("d", indicator);
.attr("d", line);

const bollinger_lower = inner.append("path")
.attr("clip-path", "url(#clipped-path)")
Expand All @@ -124,7 +120,7 @@ class Plot extends React.Component {
.attr("stroke-linecap", "round")
.attr("stroke-dasharray", "5, 5")
.attr("stroke-width", 1.5)
.attr("d", indicator);
.attr("d", line);

const sma9 = inner.append("path")
.attr("clip-path", "url(#clipped-path)")
Expand All @@ -134,7 +130,7 @@ class Plot extends React.Component {
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", indicator);
.attr("d", line);

const sma15 = inner.append("path")
.attr("clip-path", "url(#clipped-path)")
Expand All @@ -144,7 +140,7 @@ class Plot extends React.Component {
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", indicator);
.attr("d", line);

/* Plot all the buys as green dots */
const buys = inner.selectAll("scatter-buys")
Expand Down
8 changes: 7 additions & 1 deletion app/behaviours/ui/www/static/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24200,7 +24200,7 @@ var ControlPanel = function (_React$Component) {
_react2.default.createElement(
"label",
{ className: "active", htmlFor: "stop-loss" },
"Stop Loss"
"Stop Loss (%)"
)
)
)
Expand Down Expand Up @@ -24478,12 +24478,18 @@ var ControlPanel = function (_React$Component) {
var startTime = $('#start-time').val();

var capitalReg = /(^[1-9][0-9]*$)|(^[0-9]*\.[0-9]*$)/;
var stopLossReg = /(^(100)$|(^[0-9]?[0-9](\.[0-9]+)?$))/;

if (!capitalReg.exec(capital)) {
swal("Uh Oh!", "You need to enter a valid number for your starting capital.", "error");
throw "Invalid Capital";
}

if (!stopLossReg.exec(stopLoss)) {
swal("Uh Oh!", "You need to enter a valid percentage (0 - 100) for your stop loss.", "error");
throw "Invalid Stop Loss";
}

// Convert start time to epoch
startTime = new Date(startTime).getTime() / 1000;

Expand Down

0 comments on commit 511b6a5

Please sign in to comment.