forex-tester-custom-robot-api 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/IRobotApi.d.ts +1607 -0
- package/dist/IRobotApi.js +468 -0
- package/dist/Robot.d.ts +12 -0
- package/dist/Robot.js +21 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# forex-tester-custom-strategy-api
|
|
2
|
+
|
|
3
|
+
TypeScript types and runtime API for custom strategies in [Forex Tester Online](https://forextester.com/).
|
|
4
|
+
|
|
5
|
+
Install this package in a custom strategy project, extend `Robot`, then build and upload the result to FTO.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install forex-tester-custom-strategy-api
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Write a strategy
|
|
14
|
+
|
|
15
|
+
Export a default class that extends `Robot`. Implement `OnInit` and `OnTick`. The platform injects `this.api` before `OnInit` runs.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
Robot,
|
|
20
|
+
InputNumber,
|
|
21
|
+
MovingAverageType,
|
|
22
|
+
OrderSelectMode,
|
|
23
|
+
OrderType,
|
|
24
|
+
PriceType,
|
|
25
|
+
} from 'forex-tester-custom-strategy-api'
|
|
26
|
+
|
|
27
|
+
export default class MaCrossover extends Robot {
|
|
28
|
+
private fastPeriod!: InputNumber
|
|
29
|
+
private slowPeriod!: InputNumber
|
|
30
|
+
private lotSize!: InputNumber
|
|
31
|
+
|
|
32
|
+
public OnInit(): void {
|
|
33
|
+
this.api.SetRobotShortName('MA Crossover')
|
|
34
|
+
this.api.SetRobotDescription('Buys when the fast MA crosses above the slow MA.')
|
|
35
|
+
|
|
36
|
+
this.fastPeriod = this.api.CreateIntegerInput('Fast Period', 10, 1, 500, 1)
|
|
37
|
+
this.slowPeriod = this.api.CreateIntegerInput('Slow Period', 20, 1, 500, 1)
|
|
38
|
+
this.lotSize = this.api.CreateDoubleInput('Lot Size', 0.1, 0.01, 100, 0.01, 2)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public OnTick(): void {
|
|
42
|
+
const fast = this.api.GetMA(1, 0, this.fastPeriod.value, MovingAverageType.SMA, PriceType.CLOSE)
|
|
43
|
+
const slow = this.api.GetMA(1, 0, this.slowPeriod.value, MovingAverageType.SMA, PriceType.CLOSE)
|
|
44
|
+
const prevFast = this.api.GetMA(2, 0, this.fastPeriod.value, MovingAverageType.SMA, PriceType.CLOSE)
|
|
45
|
+
const prevSlow = this.api.GetMA(2, 0, this.slowPeriod.value, MovingAverageType.SMA, PriceType.CLOSE)
|
|
46
|
+
|
|
47
|
+
const crossedUp = prevFast <= prevSlow && fast > slow
|
|
48
|
+
const crossedDown = prevFast >= prevSlow && fast < slow
|
|
49
|
+
|
|
50
|
+
if (crossedUp) {
|
|
51
|
+
this.closeOpposite(OrderType.SELL)
|
|
52
|
+
this.openIfFlat(OrderType.BUY)
|
|
53
|
+
} else if (crossedDown) {
|
|
54
|
+
this.closeOpposite(OrderType.BUY)
|
|
55
|
+
this.openIfFlat(OrderType.SELL)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private openIfFlat(orderType: OrderType): void {
|
|
60
|
+
if (this.hasOrder(orderType)) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.api.PlaceOrder(
|
|
65
|
+
this.api.Symbol(),
|
|
66
|
+
orderType,
|
|
67
|
+
0,
|
|
68
|
+
this.lotSize.value,
|
|
69
|
+
0,
|
|
70
|
+
0,
|
|
71
|
+
'MA Crossover',
|
|
72
|
+
0
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private closeOpposite(orderType: OrderType): void {
|
|
77
|
+
for (let i = this.api.GetActiveOrderCount() - 1; i >= 0; i--) {
|
|
78
|
+
if (!this.api.SelectOrder(i, OrderSelectMode.POSITION)) {
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (this.api.GetOrderSymbol() === this.api.Symbol() && this.api.GetOrderType() === orderType) {
|
|
83
|
+
this.api.CloseOrder(this.api.GetOrderTicket())
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private hasOrder(orderType: OrderType): boolean {
|
|
89
|
+
for (let i = 0; i < this.api.GetActiveOrderCount(); i++) {
|
|
90
|
+
if (!this.api.SelectOrder(i, OrderSelectMode.POSITION)) {
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (this.api.GetOrderSymbol() === this.api.Symbol() && this.api.GetOrderType() === orderType) {
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`0` is the current forming bar. Use index `1` when you need a confirmed closed bar.
|
|
105
|
+
|
|
106
|
+
## Lifecycle
|
|
107
|
+
|
|
108
|
+
| Method | Required | When it runs |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `OnInit()` | yes | Once, when the strategy is created |
|
|
111
|
+
| `OnTick()` | yes | On each price update |
|
|
112
|
+
| `OnParamsChange()` | no | After the user changes inputs |
|
|
113
|
+
| `OnStart()` | no | When the strategy starts running |
|
|
114
|
+
| `OnStop()` | no | When the strategy is stopped |
|
|
115
|
+
|
|
116
|
+
## What `this.api` covers
|
|
117
|
+
|
|
118
|
+
- **Orders** — `PlaceOrder`, `ModifyOrder`, `CloseOrder`, `ClosePartial`, `CancelOrder`, `SelectOrder`, `GetOrderInfo`
|
|
119
|
+
- **Inputs** — `CreateIntegerInput`, `CreateDoubleInput`, `CreateEnumInput`, `CreateMATypeInput`, `CreateApplyToPriceInput`, and other typed input helpers
|
|
120
|
+
- **Chart data** — `Open`, `High`, `Low`, `Close`, `Volume`, `Time`, `Bars`, `Symbol`, `Timeframe`
|
|
121
|
+
- **Built-in indicators** — `GetMA`, `iMA`, `iRSI`, `iMACD`, and other `i*` helpers
|
|
122
|
+
- **Custom indicators** — `CreateIndicator`, `GetIndicatorValue`
|
|
123
|
+
- **Objects, dates, and account info** — chart objects, `FTODate`, and account/testing helpers
|
|
124
|
+
|
|
125
|
+
TypeScript types in this package are the source of truth: use autocomplete on `this.api` and the exported enums.
|
|
126
|
+
|
|
127
|
+
## Documentation
|
|
128
|
+
|
|
129
|
+
Full authoring guides, setup, and upload steps: [FTO custom strategies docs](https://fto-2.gitbook.io/fto-strategies-docs).
|