├── include ├── benchmark.h ├── dashboard.h ├── parser.h ├── sim.h ├── sim_derivative.h ├── callbacks.h ├── timeutils.h ├── debug.h ├── csvutils.h ├── misc.h └── dtypes.h ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── assets ├── algoticks_logo.png ├── derivative_example.zip ├── configs │ ├── linux │ │ ├── config.json │ │ ├── settings.json │ │ └── benchmark.json │ └── windows │ │ ├── config.json │ │ ├── settings.json │ │ └── benchmark.json └── algoticks_logo.svg ├── .travis.yml ├── tests ├── stream_to_csv.py ├── stream_to_socket.py ├── CMakeLists.txt └── algoticks_tests.c ├── .gitignore ├── src ├── algorithms │ ├── Greens.c │ ├── Reds.c │ ├── ThreeLineStrike.c │ ├── AbandonedBaby.c │ ├── TwoBlackGapping.c │ ├── EveningStar.c │ └── ThreeBlackCrows.c ├── dashboard.c ├── callbacks │ └── log.c ├── callbacks.c ├── debug.c ├── main.c ├── timeutils.c ├── benchmark.c ├── misc.c ├── sim.c ├── csvutils.c ├── sim_derivative.c └── parser.c ├── CMakeLists.txt ├── README.md └── LICENSE /include/benchmark.h: -------------------------------------------------------------------------------- 1 | void run_benchmark(char *benchmark_config_file, algoticks_settings settings); -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" 3 | } -------------------------------------------------------------------------------- /assets/algoticks_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jkotra/algoticks/HEAD/assets/algoticks_logo.png -------------------------------------------------------------------------------- /assets/derivative_example.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jkotra/algoticks/HEAD/assets/derivative_example.zip -------------------------------------------------------------------------------- /include/dashboard.h: -------------------------------------------------------------------------------- 1 | /* Declarations */ 2 | float getPnL(algoticks_dashboard dashboard); 3 | void print_dashboard(algoticks_settings *settings,algoticks_config *config,algoticks_dashboard dashboard); 4 | -------------------------------------------------------------------------------- /include/parser.h: -------------------------------------------------------------------------------- 1 | algoticks_settings parse_settings_from_json(char *filename); 2 | algoticks_config parse_config_from_json(char *filename); 3 | algoticks_benchmarkconfig parse_benchmark_from_json(char *filename); -------------------------------------------------------------------------------- /include/sim.h: -------------------------------------------------------------------------------- 1 | /* Declarations */ 2 | algoticks_simresult run_sim(algoticks_settings *settings, algoticks_config *config); 3 | algoticks_positionresult take_position(algoticks_signal signal, FILE *fp, int curr, algoticks_settings *settings, algoticks_config *config,algoticks_row lastrow); -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | before_install: 4 | - sudo apt-get -y install libjson-c-dev libzmq3-dev 5 | - sudo apt-get -y install cmake 6 | 7 | compiler: 8 | - gcc 9 | 10 | script: 11 | - cd ${TRAVIS_BUILD_DIR} 12 | - mkdir bin 13 | - cd bin 14 | - cmake .. 15 | - make -------------------------------------------------------------------------------- /include/sim_derivative.h: -------------------------------------------------------------------------------- 1 | /* Declarations */ 2 | algoticks_simresult run_sim_w_derivative(algoticks_settings *settings, algoticks_config *config); 3 | algoticks_positionresult take_position_w_derivative(algoticks_signal signal, FILE *index_f, FILE *derivative_f, algoticks_settings *settings, algoticks_config *config, algoticks_row lastrow); -------------------------------------------------------------------------------- /include/callbacks.h: -------------------------------------------------------------------------------- 1 | int load_callbacks(algoticks_config *config); 2 | int send_callbacks(algoticks_event events); 3 | int close_callbacks(); 4 | algoticks_event make_event_from_positionresult(algoticks_positionresult positionresult); 5 | algoticks_event make_event_from_signal(algoticks_signal signal); 6 | algoticks_event make_event_from_position(algoticks_row pos_storage, algoticks_dashboard dashboard); 7 | -------------------------------------------------------------------------------- /include/timeutils.h: -------------------------------------------------------------------------------- 1 | int is_date_over_or_eq_intraday(char *date, int intraday_hour, int intraday_min); 2 | int is_date_after(char *date_a, char *date_b); 3 | int is_date_before(char *date_a, char *date_b); 4 | int get_time_with_sscanf_from_string(char* date, struct tm *time_struct); 5 | int sync_curr(algoticks_settings *settings, algoticks_config *config, FILE *f, char* fname, char *date, int seek_offset, int debug); -------------------------------------------------------------------------------- /assets/configs/linux/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "algo": "algorithms/Reds.so", 3 | "datasource": "example.csv", 4 | "symbol": "_EXAMPLE_", 5 | "candles": 3, 6 | "interval": 0, 7 | 8 | "quantity": 100, 9 | "target": 5, 10 | "stoploss": 7, 11 | "is_trailing_sl": false, 12 | "trailing_sl_val": 1, 13 | 14 | "sliding": true, 15 | "intraday": false, 16 | "skip_header": true 17 | 18 | } 19 | -------------------------------------------------------------------------------- /assets/configs/windows/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "algo": "algorithms/Reds.dll", 3 | "datasource": "example.csv", 4 | "symbol": "_EXAMPLE_", 5 | "candles": 3, 6 | "interval": 0, 7 | 8 | "quantity": 100, 9 | "target": 5, 10 | "stoploss": 7, 11 | "is_trailing_sl": false, 12 | "trailing_sl_val": 1, 13 | 14 | "sliding": true, 15 | "intraday": false, 16 | "skip_header": true 17 | 18 | } 19 | -------------------------------------------------------------------------------- /assets/configs/linux/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "print": true, 3 | "colors": true, 4 | "debug": false, 5 | "debug_level": 1, 6 | 7 | "is_live_data": false, 8 | 9 | "derivative": false, 10 | "benchmark": false, 11 | 12 | "config_f": "config.json", 13 | "benchmark_f": "benchmark.json", 14 | 15 | "socket": false, 16 | "socket_port": "5757", 17 | 18 | "intraday_hour": 15, 19 | "intraday_min": 15 20 | } 21 | -------------------------------------------------------------------------------- /assets/configs/windows/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "print": true, 3 | "colors": true, 4 | "debug": false, 5 | "debug_level": 1, 6 | 7 | "is_live_data": false, 8 | 9 | "derivative": false, 10 | "benchmark": false, 11 | 12 | "config_f": "config.json", 13 | "benchmark_f": "benchmark.json", 14 | 15 | "socket": false, 16 | "socket_port": "5757", 17 | 18 | "intraday_hour": 15, 19 | "intraday_min": 15 20 | } 21 | -------------------------------------------------------------------------------- /tests/stream_to_csv.py: -------------------------------------------------------------------------------- 1 | # @author: Jagadeesh Kotra 2 | 3 | 4 | import time 5 | import datetime 6 | import time 7 | 8 | 9 | while True: 10 | try: 11 | for line in open("AAPL.csv", "r").readlines(): 12 | csv_file = open("../bin/debug/example.csv", "a") 13 | csv_file.write(line) 14 | csv_file.close() 15 | time.sleep(1) 16 | 17 | except KeyboardInterrupt: 18 | print("Bye!") 19 | exit(0) -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "algoticks", 8 | "type": "shell", 9 | "command": "make", 10 | "problemMatcher": [], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /assets/configs/linux/benchmark.json: -------------------------------------------------------------------------------- 1 | { 2 | "algo": ["algorithms/Greens.so", "algorithms/Reds.so"], 3 | "datasource": ["example.csv"], 4 | "symbol": "EXAMPLE", 5 | "candles": [4,6,8], 6 | "interval": [5,10,15], 7 | 8 | "quantity": [10], 9 | "target": [1.5,2,2.5], 10 | "stoploss": [2,2.5,3,3.5], 11 | "is_trailing_sl": [true,false], 12 | "trailing_sl_val": [1,2,3], 13 | 14 | "sliding": [true, false], 15 | "intraday": [true,false], 16 | "skip_header": true 17 | } 18 | -------------------------------------------------------------------------------- /assets/configs/windows/benchmark.json: -------------------------------------------------------------------------------- 1 | { 2 | "algo": ["algorithms/Greens.dll", "algorithms/Reds.dll"], 3 | "datasource": ["example.csv"], 4 | "symbol": "EXAMPLE", 5 | "candles": [4,6,8], 6 | "interval": [5,10,15], 7 | 8 | "quantity": [10], 9 | "target": [1.5,2,2.5], 10 | "stoploss": [2,2.5,3,3.5], 11 | "is_trailing_sl": [true,false], 12 | "trailing_sl_val": [1,2,3], 13 | 14 | "sliding": [true, false], 15 | "intraday": [true,false], 16 | "skip_header": true 17 | } 18 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | /* Declarations */ 2 | void debug_msg(bool settings_debug, int settings_debug_level, int msg_debug_level, char *file_name, const char *function, int line_n, char *message); 3 | void debug_msg_simple(char* msg); 4 | /* struct debug */ 5 | void print_config_struct(algoticks_config *config); 6 | void print_dashboard_struct(algoticks_dashboard *dashboard); 7 | void print_signal_struct(algoticks_signal *signal); 8 | void print_simresult_struct(algoticks_simresult *simresult); 9 | void print_row_struct(algoticks_row *row); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/** 2 | *.o 3 | *.csv 4 | !example.csv 5 | 6 | output.log 7 | debug.log 8 | 9 | tests/** 10 | 11 | #tests folder exceptions 12 | !tests/algoticks_tests.c 13 | !tests/CMakeLists.txt 14 | !stream_to_csv.py 15 | !stream_to_socket.py 16 | !tests/AAPL.csv 17 | !assets/example_DOHLC.csv 18 | !assets/example_OCDHL.csv 19 | !assets/example_ODHLC.csv 20 | 21 | build/** 22 | CMakeLists.txt.user 23 | CMakeCache.txt 24 | CMakeFiles 25 | CMakeScripts 26 | Testing 27 | Makefile 28 | cmake_install.cmake 29 | install_manifest.txt 30 | compile_commands.json 31 | CTestTestfile.cmake 32 | _deps -------------------------------------------------------------------------------- /tests/stream_to_socket.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import socket 4 | import time 5 | import argparse 6 | 7 | parser = argparse.ArgumentParser() 8 | 9 | parser.add_argument('-a',dest="a", type=str, default="127.0.0.1") 10 | parser.add_argument('-p',dest="p", type=int, default=5757) 11 | 12 | 13 | args = parser.parse_args() 14 | 15 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 16 | s.connect((args.a, args.p)) 17 | 18 | for line in open("AAPL.csv", "r").readlines(): 19 | line = bytes("{}\0".format(line), encoding="utf-8") 20 | s.send(line) 21 | print(line) 22 | time.sleep(1) -------------------------------------------------------------------------------- /include/csvutils.h: -------------------------------------------------------------------------------- 1 | #define MAXCHARPERLINE 1024 2 | #define MAXCSVHEAD 10 3 | 4 | /* Declarations */ 5 | void reset_header_skip(); 6 | void* socket_init(char* port); 7 | int change_in_modified_date(char* filename); 8 | int reopen_datasource(char* filename, FILE** fp, char* mode); 9 | algoticks_row tokenize_row(char *row); 10 | void set_ohlcv_as_header(); 11 | int process_csv_header(algoticks_settings *settings, char *row); 12 | int read_csv(algoticks_settings *settings,algoticks_config *config, FILE *fp, char *fname, algoticks_row *storage, int seek_offset); 13 | 14 | 15 | //optional optimization 16 | int is_quoted(char *str); 17 | int check_row_integrity(algoticks_row *row); -------------------------------------------------------------------------------- /src/algorithms/Greens.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../../include/dtypes.h" 5 | 6 | #ifdef _WIN32 7 | __declspec(dllexport) 8 | #endif 9 | algoticks_signal analyze(algoticks_row *series, int n_candles){ 10 | 11 | /* This is a buy only algo indicator */ 12 | 13 | struct Signal signal; 14 | memset(&signal, 0, sizeof(signal)); 15 | 16 | for (int i = 1; i < n_candles; i++) 17 | { 18 | 19 | if(series[i].close > series[i-1].close){ 20 | signal.buy = true; 21 | } 22 | else { 23 | signal.neutral = true; 24 | signal.sell = false; 25 | signal.buy = false; 26 | break; 27 | } 28 | } 29 | 30 | return signal; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/algorithms/Reds.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../../include/dtypes.h" 5 | 6 | #ifdef _WIN32 7 | __declspec(dllexport) 8 | #endif 9 | algoticks_signal analyze(algoticks_row *series, int n_candles){ 10 | 11 | /* This is a sell only algo indicator */ 12 | 13 | 14 | struct Signal signal; 15 | memset(&signal, 0, sizeof(signal)); 16 | 17 | for (int i = 1; i < n_candles; i++) 18 | { 19 | 20 | if(series[i].close < series[i-1].close){ 21 | signal.sell = true; 22 | } 23 | else { 24 | signal.neutral = true; 25 | signal.sell = false; 26 | signal.buy = false; 27 | break; 28 | } 29 | } 30 | 31 | return signal; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /include/misc.h: -------------------------------------------------------------------------------- 1 | /* Declarations */ 2 | algoticks_cb_l load_cb(char *algo); 3 | algo_func load_algo_func(char *algo); 4 | void close_algo_func(); 5 | void write_simresult_to_csv(algoticks_simresult *simresult); 6 | void create_setting_config_benchmark_files(int type); 7 | int is_file_exists(const char * filename); 8 | void filter_boundaries(algoticks_config *config, int is_short); 9 | int is_target_hit(algoticks_dashboard dashboard, float target); 10 | int is_stoploss_hit(algoticks_dashboard dashboard, float stoploss); 11 | float brokerage_calc(); 12 | void convert_to_lowercase(char *str); 13 | void free_algoticks_config(algoticks_config *config); 14 | void free_algoticks_settings(algoticks_settings *settings); 15 | void free_algoticks_benchmark(algoticks_benchmarkconfig *benchmark); 16 | bool is_mapped(int key, char *header_name); 17 | char *get_relative_header_mapping(int index); 18 | 19 | //optional optimization 20 | void chomp(char *s); 21 | void remove_quotes(char *str); -------------------------------------------------------------------------------- /src/algorithms/ThreeLineStrike.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../../include/dtypes.h" 6 | 7 | 8 | /* 9 | 10 | 3TECHNICAL DETAILS# 11 | 12 | NAME: Three Line Strike (ThreeLineStrike.so / ThreeLineStrike.dll) 13 | MIN REQ CANDLES: 4 14 | */ 15 | 16 | #ifdef _WIN32 17 | __declspec(dllexport) 18 | #endif 19 | algoticks_signal analyze(algoticks_row *series, int n_candles){ 20 | 21 | 22 | 23 | if (n_candles < 4){ 24 | printf("Error: ThreeLineStrike requires atleast 4 candles.\n"); 25 | exit(1); 26 | } 27 | 28 | 29 | struct Signal signal; 30 | memset(&signal, 0, sizeof(signal)); 31 | signal.neutral = true; 32 | 33 | if (series[0].low > series[1].low){ 34 | if (series[1].low > series[2].low){ 35 | if (series[3].close >= series[0].high){ 36 | 37 | signal.buy = true; 38 | signal.sell = false; 39 | signal.neutral = false; 40 | 41 | } 42 | 43 | } 44 | } 45 | 46 | return signal; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /assets/algoticks_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/algorithms/AbandonedBaby.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../../include/dtypes.h" 6 | 7 | /* 8 | 9 | 3TECHNICAL DETAILS# 10 | 11 | NAME: Abandoned Baby (AbandonedBaby.so / AbandonedBaby.dll) 12 | MIN REQ CANDLES: 6 13 | */ 14 | 15 | 16 | #ifdef _WIN32 17 | __declspec(dllexport) 18 | #endif 19 | algoticks_signal analyze(algoticks_row *series, int n_candles) 20 | { 21 | 22 | 23 | if (n_candles < 6) 24 | { 25 | printf("Error: AbandonedBaby requires atleast 6 candles.\n"); 26 | exit(1); 27 | } 28 | 29 | struct Signal signal; 30 | memset(&signal, 0, sizeof(signal)); 31 | signal.neutral = true; 32 | 33 | for (int i = 1; i < 3; i++) 34 | { 35 | if (series[i].low > series[i - 1].low) 36 | { 37 | return signal; 38 | } 39 | } 40 | 41 | //abandoned baby 42 | if (series[3].low > series[4].low && series[4].open != series[3].close) 43 | { 44 | if (series[5].close > series[4].close && series[5].close > series[3].high) 45 | { 46 | 47 | signal.buy = true; 48 | signal.sell = false; 49 | signal.neutral = false; 50 | } 51 | } 52 | 53 | return signal; 54 | } -------------------------------------------------------------------------------- /src/algorithms/TwoBlackGapping.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../../include/dtypes.h" 6 | 7 | /* 8 | 9 | 3TECHNICAL DETAILS# 10 | 11 | NAME: Two Black Gapping (TwoBlackGapping.so / TwoBlackGapping.dll) 12 | MIN REQ CANDLES: 6 13 | */ 14 | 15 | #ifdef _WIN32 16 | __declspec(dllexport) 17 | #endif 18 | 19 | algoticks_signal analyze(algoticks_row *series, int n_candles) 20 | { 21 | 22 | if (n_candles < 6) 23 | { 24 | printf("Error: TwoBlackGapping requires atleast 6 candles.\n"); 25 | exit(1); 26 | } 27 | 28 | struct Signal signal; 29 | memset(&signal, 0, sizeof(signal)); 30 | signal.neutral = true; 31 | 32 | if (series[1].close > series[0].close && series[1].high > series[0].high) 33 | { 34 | if (series[2].close > series[1].close && series[2].high > series[1].high) 35 | { 36 | if (series[3].close < series[2].close) 37 | { 38 | 39 | if (series[4].close < series[3].close) 40 | { 41 | if (series[5].open != series[4].close) 42 | { 43 | signal.sell = true; 44 | signal.buy = false; 45 | signal.neutral = false; 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | return signal; 53 | } 54 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(algoticks_tests VERSION 1.0 LANGUAGES C) 3 | set(CMAKE_C_STANDARD 11) 4 | 5 | if (WIN32) 6 | include_directories(C:/vcpkg/installed/x64-windows/include) 7 | link_directories(C:/vcpkg/installed/x64-windows/lib) 8 | endif() 9 | 10 | add_executable(algoticks_tests algoticks_tests.c ../src/misc.c ../src/parser.c ../src/dashboard.c ../src/timeutils.c ../src/csvutils.c ../src/debug.c) 11 | 12 | target_link_libraries(algoticks_tests PRIVATE check) 13 | target_link_libraries(algoticks_tests PRIVATE json-c) 14 | target_link_libraries(algoticks_tests PRIVATE zmq) 15 | target_link_libraries(algoticks_tests PRIVATE uv) 16 | 17 | if(NOT WIN32) 18 | target_link_libraries(algoticks_tests PRIVATE dl) 19 | endif() 20 | 21 | if(WIN32) 22 | set(config_dir ${PROJECT_SOURCE_DIR}/../assets/configs/windows) 23 | else() 24 | set(config_dir ${PROJECT_SOURCE_DIR}/../assets/configs/linux) 25 | endif() 26 | 27 | #copy configs 28 | file(COPY ${config_dir}/config.json 29 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 30 | file(COPY ${config_dir}/settings.json 31 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 32 | 33 | file(COPY ${config_dir}/../../example_DOHLC.csv 34 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 35 | file(COPY ${config_dir}/../../example_OCDHL.csv 36 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 37 | file(COPY ${config_dir}/../../example_ODHLC.csv 38 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -------------------------------------------------------------------------------- /src/algorithms/EveningStar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../../include/dtypes.h" 6 | 7 | /* 8 | 9 | 3TECHNICAL DETAILS# 10 | 11 | NAME: EveningStar (EveningStar.so / EveningStar.dll) 12 | MIN REQ CANDLES: 4 13 | */ 14 | 15 | int tall_candle_th = 3; 16 | 17 | #ifdef _WIN32 18 | __declspec(dllexport) 19 | #endif 20 | algoticks_signal analyze(algoticks_row *series, int n_candles) 21 | { 22 | 23 | 24 | if (n_candles < 4) 25 | { 26 | printf("Error: EveningStar requires atleast 4 candles.\n"); 27 | exit(1); 28 | } 29 | 30 | struct Signal signal; 31 | memset(&signal, 0, sizeof(signal)); 32 | signal.neutral = true; 33 | 34 | if (series[1].close > series[0].close) 35 | { 36 | 37 | //check if c0 is red 38 | int c0_red = (series[0].open - series[0].close) < 0; 39 | 40 | if (c0_red) 41 | { 42 | if ((series[1].open - series[1].close) >= (series[0].close - series[0].open) * tall_candle_th) 43 | { 44 | 45 | if (series[2].close < series[1].close) 46 | { 47 | if (series[3].close < series[2].close) 48 | { 49 | signal.sell = true; 50 | signal.buy = false; 51 | signal.neutral = false; 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | return signal; 59 | } 60 | -------------------------------------------------------------------------------- /src/algorithms/ThreeBlackCrows.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../../include/dtypes.h" 6 | 7 | /* 8 | 9 | 3TECHNICAL DETAILS# 10 | 11 | NAME: Three Black Crows (ThreeBlackCrows.so / ThreeBlackCrows.dll) 12 | MIN REQ CANDLES: 6 13 | */ 14 | 15 | #ifdef _WIN32 16 | __declspec(dllexport) 17 | #endif 18 | algoticks_signal analyze(algoticks_row *series, int n_candles) 19 | { 20 | 21 | 22 | if (n_candles < 6) 23 | { 24 | printf("Error: ThreeBlackCrows requires atleast 6 candles.\n"); 25 | exit(1); 26 | } 27 | 28 | struct Signal signal; 29 | memset(&signal, 0, sizeof(signal)); 30 | signal.neutral = true; 31 | 32 | if (series[1].high > series[0].high && series[1].close > series[0].close) 33 | { 34 | if (series[2].high > series[1].high && series[1].close > series[0].close) 35 | { 36 | if (series[3].close <= series[2].close) 37 | { 38 | if (series[3].close <= series[2].close) 39 | { 40 | 41 | if (series[4].close <= series[3].close) 42 | { 43 | if (series[5].close <= series[4].close) 44 | { 45 | signal.sell = true; 46 | signal.buy = false; 47 | signal.neutral = false; 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | return signal; 56 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": "(gdb) Launch (Linux)", 10 | "type": "cppdbg", 11 | "request": "launch", 12 | "program": "${workspaceFolder}/bin/debug/algoticks", 13 | "args": ["-D","1"], 14 | "stopAtEntry": false, 15 | "cwd": "${workspaceFolder}/bin/debug/", 16 | "environment": [], 17 | "externalConsole": false, 18 | "MIMode": "gdb", 19 | "setupCommands": [ 20 | { 21 | "description": "Enable pretty-printing for gdb", 22 | "text": "-enable-pretty-printing", 23 | "ignoreFailures": true 24 | } 25 | ] 26 | }, 27 | 28 | { 29 | "name": "(gdb) Launch (Windows)", 30 | "type": "cppdbg", 31 | "request": "launch", 32 | "program": "${workspaceFolder}/bin/debug/algoticks.exe", 33 | "args": [], 34 | "stopAtEntry": false, 35 | "cwd": "${workspaceFolder}/bin/debug/", 36 | "environment": [], 37 | "externalConsole": false, 38 | "MIMode": "gdb", 39 | "setupCommands": [ 40 | { 41 | "description": "Enable pretty-printing for gdb", 42 | "text": "-enable-pretty-printing", 43 | "ignoreFailures": true 44 | } 45 | ] 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /src/dashboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../include/dtypes.h" 5 | #include "../include/debug.h" 6 | #include "../include/dashboard.h" 7 | 8 | 9 | char color_reset[12] = "\033[0m"; 10 | 11 | float getPnL(algoticks_dashboard dashboard){ 12 | 13 | int isshort = (dashboard.is_short == true); 14 | 15 | if (isshort){ 16 | return -(dashboard.b - dashboard.a) * dashboard.q; 17 | } 18 | else{ 19 | return (dashboard.b - dashboard.a) * dashboard.q; 20 | } 21 | 22 | } 23 | 24 | void print_dashboard(algoticks_settings *settings,algoticks_config *config,algoticks_dashboard dashboard){ 25 | char buffer[1024]; 26 | char pnlcolor[12]; 27 | char pnl_s[128]; 28 | float percentage_change; 29 | 30 | float pnl = getPnL(dashboard); 31 | float pnl_i = pnl / dashboard.q; 32 | 33 | percentage_change = ((pnl)/(dashboard.q * dashboard.a)) * 100; 34 | 35 | if (settings->colors == true){ 36 | if(pnl > 0){ 37 | strcpy(pnlcolor, "\033[0;32m"); 38 | } 39 | else { 40 | strcpy(pnlcolor, "\033[0;31m"); 41 | } 42 | if (pnl > 0){ 43 | //^ added space to make it look uniform wrt. negative values 44 | sprintf(pnl_s, "%s %f\t (%f%%)\t %f %s", pnlcolor, pnl, percentage_change, pnl_i, color_reset); 45 | } 46 | else{ 47 | sprintf(pnl_s, "%s %f\t (%f%%)\t%f %s", pnlcolor, pnl, percentage_change, pnl_i, color_reset); 48 | } 49 | }else{ 50 | sprintf(pnl_s, "%f(%f%%) %f", pnl, percentage_change, pnl_i); 51 | } 52 | 53 | sprintf(buffer, "%s\t%s\t%f\t%f\t%s", dashboard.date, config->symbol, dashboard.a, dashboard.b, pnl_s); 54 | printf("%s\n", buffer); 55 | 56 | } -------------------------------------------------------------------------------- /src/callbacks/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../../include/dtypes.h" 7 | #include "../include/debug.h" 8 | 9 | FILE *fp; 10 | char hrt[32]; 11 | 12 | void chomp(char *s) 13 | { 14 | /* This removes newline at end of string */ 15 | while (*s && *s != '\n' && *s != '\r') 16 | s++; 17 | *s = 0; 18 | } 19 | 20 | void get_hrt(char *hrt) 21 | { 22 | time_t now; 23 | 24 | //get time 25 | time(&now); 26 | 27 | sprintf(hrt, "%s", ctime(&now)); 28 | 29 | #ifdef CHOMP 30 | chomp(hrt); 31 | #endif 32 | } 33 | 34 | #ifdef _WIN32 35 | __declspec(dllexport) 36 | #endif 37 | int callback_f(algoticks_event *event) 38 | { 39 | 40 | if (fp == NULL) 41 | { 42 | fp = fopen("total.log", "w"); 43 | } 44 | else if(fp != NULL){ 45 | fp = fopen("total.log", "a"); 46 | } 47 | 48 | get_hrt(hrt); 49 | 50 | if (event->signal.buy != false) 51 | { 52 | fprintf(fp, "{\"date\": \"%s\", \"signal\": \"Buy\" }\n", hrt); 53 | } 54 | 55 | else if (event->signal.sell != false) 56 | { 57 | fprintf(fp, "{\"date\": \"%s\", \"signal\": \"Sell\" }\n", hrt); 58 | } 59 | 60 | else if (event->from_pos) 61 | { 62 | fprintf(fp, "{\"date\": \"%s\", \"positionPnL\": %f }\n", hrt, event->pnl); 63 | } 64 | 65 | else if (event->t_h) 66 | { 67 | fprintf(fp, "{\"date\": \"%s\", \"Hit\": \"Target\", \"simulationPnl\": %f }\n", hrt, event->pnl); 68 | } 69 | 70 | else if (event->sl_h) 71 | { 72 | fprintf(fp, "{\"date\": \"%s\", \"Hit\": \"SL\", \"simulationPnl\": %f }\n", hrt, event->pnl); 73 | } 74 | 75 | else if (event->tsl) 76 | { 77 | fprintf(fp, "{\"date\": \"%s\", \"Hit\": \"TSL\", \"T\": %f, \"SL\": %f }\n", hrt, event->tsl_t, event->tsl_sl); 78 | } 79 | 80 | fclose(fp); 81 | 82 | 83 | return true; 84 | } -------------------------------------------------------------------------------- /src/callbacks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../include/dtypes.h" 6 | #include "../include/misc.h" 7 | #include "../include/dashboard.h" 8 | 9 | #ifndef _WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | callback_func *cb; 16 | void **cb_h; 17 | int n; 18 | 19 | 20 | int load_callbacks(algoticks_config *config){ 21 | 22 | if (config->n_callbacks > 0){ 23 | cb = (callback_func *) malloc(config->n_callbacks * sizeof(callback_func)); 24 | cb_h = (void **) malloc(config->n_callbacks * sizeof(void)); 25 | n = config->n_callbacks; 26 | } 27 | 28 | for (int i = 0; i < n; i++) 29 | { 30 | algoticks_cb_l l; 31 | l = load_cb(config->callbacks[i]); 32 | cb[i] = l.callback_func; 33 | cb_h[i] = l.handle; 34 | } 35 | 36 | return true; 37 | 38 | } 39 | int send_callbacks(algoticks_event event){ 40 | 41 | for (int i = 0; i < n; i++) 42 | { 43 | 44 | ((callback_func)cb[i]) (&event); 45 | 46 | } 47 | 48 | return true; 49 | 50 | } 51 | 52 | int close_callbacks(){ 53 | 54 | for (int i = 0; i < n; i++) 55 | { 56 | #ifdef _WIN32 57 | FreeLibrary(cb_h[i]); 58 | #else 59 | dlclose(cb_h[i]); 60 | #endif 61 | } 62 | 63 | free(cb); 64 | free(cb_h); 65 | 66 | return true; 67 | 68 | } 69 | 70 | algoticks_event make_event_from_positionresult(algoticks_positionresult positionresult){ 71 | 72 | algoticks_event ev = {0}; 73 | if (strcmp(positionresult.hit_type, "SL") == 0) { 74 | ev.sl_h = true; 75 | } 76 | 77 | if (strcmp(positionresult.hit_type, "T") == 0) { 78 | ev.t_h = true; 79 | } 80 | 81 | ev.pnl = positionresult.pnl; 82 | return ev; 83 | } 84 | 85 | algoticks_event make_event_from_signal(algoticks_signal signal){ 86 | 87 | algoticks_event ev = {0}; 88 | ev.signal = signal; 89 | return ev; 90 | } 91 | 92 | algoticks_event make_event_from_position(algoticks_row pos_storage, algoticks_dashboard dashboard){ 93 | 94 | algoticks_event ev = {0}; 95 | 96 | strncpy(ev.date, pos_storage.date, 32); 97 | ev.a = dashboard.a; 98 | ev.b = dashboard.b; 99 | ev.pnl = getPnL(dashboard); 100 | ev.from_pos = true; 101 | 102 | return ev; 103 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(algoticks VERSION 1.0 LANGUAGES C) 3 | set(CMAKE_C_STANDARD 11) 4 | 5 | #uncomment below line to enable Optimizations 6 | #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCHOMP -DQUOTED_CHECK -DCHECK_ROW_INTEGRITY") 7 | 8 | if (NOT CMAKE_BUILD_TYPE) 9 | message("No Build type specified. Default is Debug.") 10 | set(CMAKE_BUILD_TYPE Debug) 11 | endif() 12 | 13 | if (CMAKE_BUILD_TYPE MATCHES Release) 14 | message("Release Type = Release") 15 | set(CMAKE_CURRENT_BINARY_DIR "release") 16 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -O3") 17 | endif() 18 | 19 | #if debug in arg, change folder name to "debug" 20 | if(CMAKE_BUILD_TYPE MATCHES Debug) 21 | message("Release Type = Debug") 22 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") 23 | set(CMAKE_CURRENT_BINARY_DIR "debug") 24 | ENDIF(CMAKE_BUILD_TYPE MATCHES Debug) 25 | 26 | 27 | include_directories(include /usr/include) 28 | 29 | #GLOB all source files! 30 | file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.c) 31 | 32 | 33 | #############ALGORITHMS######################### 34 | 35 | #GLOB all source files! 36 | file(GLOB ALGORITHMS ${PROJECT_SOURCE_DIR}/src/algorithms/*.c) 37 | 38 | foreach(algo ${ALGORITHMS}) 39 | get_filename_component(algo_name ${algo} NAME_WLE) 40 | message("Algorithm:" ${algo_name}) 41 | add_library(${algo_name} MODULE src/algorithms/${algo_name}.c) 42 | set_target_properties(${algo_name} 43 | PROPERTIES 44 | PREFIX "" 45 | LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/algorithms/ 46 | ) 47 | endforeach() 48 | 49 | ###############//END//######################### 50 | 51 | #############CALLBACKS######################### 52 | 53 | #GLOB all source files! 54 | file(GLOB CALLBACKS ${PROJECT_SOURCE_DIR}/src/callbacks/*.c) 55 | 56 | foreach(cb ${CALLBACKS}) 57 | get_filename_component(cb_name ${cb} NAME_WLE) 58 | message("Callback:" ${cb_name}) 59 | add_library(${cb_name} MODULE src/callbacks/${cb_name}.c) 60 | set_target_properties(${cb_name} 61 | PROPERTIES 62 | PREFIX "" 63 | LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/callbacks/ 64 | ) 65 | endforeach() 66 | 67 | ###############//END//###################### 68 | 69 | 70 | add_executable(algoticks ${SRC_FILES}) 71 | 72 | #link required libs 73 | target_link_libraries(algoticks PRIVATE json-c) 74 | target_link_libraries(algoticks PRIVATE zmq) 75 | target_link_libraries(algoticks PRIVATE uv) 76 | 77 | if(NOT WIN32) 78 | target_link_libraries(algoticks PRIVATE dl) 79 | endif() 80 | 81 | #set output to bin/ 82 | set_target_properties(algoticks 83 | PROPERTIES 84 | ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/archive/ 85 | LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib/ 86 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ 87 | ) 88 | 89 | if(WIN32) 90 | set(config_dir ${PROJECT_SOURCE_DIR}/assets/configs/windows) 91 | else() 92 | set(config_dir ${PROJECT_SOURCE_DIR}/assets/configs/linux) 93 | endif() 94 | 95 | #copy configs to bin/ 96 | file(COPY ${config_dir}/config.json 97 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 98 | file(COPY ${config_dir}/settings.json 99 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 100 | file(COPY ${config_dir}/benchmark.json 101 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 102 | file(COPY ${PROJECT_SOURCE_DIR}/assets/example.csv 103 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![](assets/algoticks_logo.png) 4 | 5 | 6 | # Algoticks 7 | 8 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Build Status](https://travis-ci.org/jkotra/algoticks.svg?branch=master)](https://travis-ci.org/jkotra/algoticks) 9 | 10 | --- 11 |
 12 | Algoticks is an algorithmic trading simulator written in C.
 13 | 
14 | 15 | 16 | [READ: Getting started with Algoticks](https://stdin.top/posts/getting-started-with-algoticks/) 17 | 18 | [READ: Writing an Algorithm for Algoticks](https://stdin.top/posts/write-algo-for-algoticks/) 19 | 20 |
21 | 22 | --- 23 | 24 | ## Building 25 | 26 | ### Optimizations 27 | 28 | Some optimizations are disabled by default to improve speed and efficiency. 29 | 30 | ``` 31 | #uncomment below line to enable Optimizations 32 | #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCHOMP -DQUOTED_CHECK -DCHECK_ROW_INTEGRITY") 33 | ``` 34 | 35 | ### Release 36 | 37 | Recommended Compiler: `GCC` 38 | 39 | Recommended Standard: `C11` 40 | 41 | Dependencies: 42 | - [`JSON-C`](https://github.com/json-c/json-c) 43 | - [`ZeroMQ`](https://github.com/zeromq/libzmq) 44 | - [`libuv`](https://github.com/libuv/libuv) 45 | 46 | ``` 47 | mkdir bin 48 | cd bin 49 | cmake .. 50 | make 51 | ``` 52 | 53 | upon successful compilation, the binary executable along with config files are found in `release` (`bin/release`) folder. 54 | 55 | ### Debug 56 | 57 | ``` 58 | mkdir bin 59 | cd bin 60 | cmake -DCMAKE_BUILD_TYPE=Debug .. 61 | make 62 | ``` 63 | 64 | upon successful compilation, the binary executable along with config files are found in `debug` (`bin/debug`) folder. 65 | 66 | --- 67 | 68 | 69 | ### Building on Windows (experimental) 70 | 71 | 1. Install [MSYS2](https://www.msys2.org/) 72 | 2. Install [CMake for Windows.](https://cmake.org/download/) 73 | 2. Add the following to System PATH: 74 | ``` 75 | C:\msys64\mingw64\include 76 | C:\msys64\mingw64\bin 77 | C:\msys64\mingw64\lib 78 | C:\msys64\usr\bin 79 | ``` 80 | 81 | Run the following commands in `mingw64.exe` terminal: 82 | ``` 83 | pacman -S base-devel mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb mingw-w64-x86_64-json-c mingw-w64-x86_64-zeromq mingw-w64-x86_64-libuv 84 | ``` 85 | 86 | Run the following commands from repository root directory: 87 | ``` 88 | mkdir bin 89 | cd bin 90 | cmake -G "MinGW Makefiles" .. 91 | mingw32-make.exe 92 | ``` 93 | 94 | --- 95 | 96 | ## Usage 97 | 98 | ``` 99 | Usage: algoticks [OPTION...] 100 | Algoticks - algorithmic trading simulator. 101 | 102 | -b, --benchmark Benchmark mode 103 | -B, --benchmarkfile=FILE Benchmark file. 104 | -C, --configfile=FILE Config file. 105 | -d, --derivative Derivative mode 106 | -D, --debug=LEVEL Debug mode. 107 | -l, --live Live mode. Wait for date at EOF 108 | -s, --socket=PORT Open socket to receive data at EOF 109 | -S, --settingsfile=FILE settings file. 110 | -?, --help Give this help list 111 | --usage Give a short usage message 112 | -V, --version Print program version 113 | 114 | Mandatory or optional arguments to long options are also mandatory or optional 115 | for any corresponding short options. 116 | 117 | Report bugs to . 118 | 119 | 120 | ``` 121 | 122 | 123 | ### Timeline 124 | 125 | Start of development: 13-Jun-2020 126 | 127 | Repository made public: 02-Jul-2020 128 | 129 | --- 130 | 131 | ### Credits 132 | 133 | Icons made by Eucalyp from www.flaticon.com 134 | -------------------------------------------------------------------------------- /include/dtypes.h: -------------------------------------------------------------------------------- 1 | typedef struct Settings 2 | { 3 | int print; 4 | int colors; 5 | int debug; 6 | int debug_level; 7 | 8 | char *config_f; 9 | char *benchmark_f; 10 | 11 | int is_live_data; 12 | int is_live_data_socket; 13 | char *socket_port; 14 | int is_derivative; 15 | int is_benchmark; /* for windows usage */ 16 | 17 | int intraday_hour; 18 | int intraday_min; 19 | 20 | }algoticks_settings; 21 | 22 | typedef struct Dervative{ 23 | char *derivative_datasource; 24 | int derivative_interval; 25 | }algoticks_derivative; 26 | 27 | 28 | 29 | typedef struct Config 30 | { 31 | char *algo; 32 | char *datasource; 33 | algoticks_derivative derivative; 34 | char *symbol; 35 | int candles; 36 | int interval; 37 | 38 | char **callbacks; 39 | int n_callbacks; 40 | 41 | double target; 42 | double stoploss; 43 | int is_trailing_sl; /* FLAG */ 44 | double trailing_sl_val; 45 | int quantity; 46 | 47 | int sliding; /* FLAG */ 48 | int intraday; /* FLAG */ 49 | 50 | int skip_header; /* FLAG */ 51 | }algoticks_config; 52 | 53 | typedef struct BenchmarkConfig 54 | { 55 | 56 | char *symbol; 57 | 58 | int n_algo; 59 | char **algo_arr; 60 | 61 | int n_datasource; 62 | char **datasource_arr; 63 | 64 | algoticks_derivative derivative; 65 | 66 | int n_candles; 67 | int *candles_arr; 68 | 69 | int n_interval; 70 | int *interval_arr; 71 | 72 | int n_target; 73 | double *target_arr; 74 | 75 | int n_stoploss; 76 | double *stoploss_arr; 77 | 78 | int n_is_trailing_sl; 79 | int *is_trailing_sl; /* array of bool */ 80 | 81 | int n_trailing_sl_val; 82 | double *trailing_sl_val_arr; 83 | 84 | int n_quantity; 85 | int *quantity_arr; 86 | 87 | int n_sliding; 88 | int *sliding; /* array of bool */ 89 | 90 | int n_intraday; 91 | int *intraday; /* array of bool */ 92 | 93 | int skip_header; 94 | 95 | }algoticks_benchmarkconfig; 96 | 97 | typedef struct Dashboard 98 | { 99 | char date[32]; 100 | float a; 101 | float b; 102 | float q; 103 | 104 | float pnl; 105 | int is_short; 106 | }algoticks_dashboard; 107 | 108 | typedef struct row_ti{ 109 | 110 | int is_ti1_p; 111 | float ti1; 112 | 113 | int is_ti2_p; 114 | float ti2; 115 | 116 | int is_ti3_p; 117 | float ti3; 118 | 119 | int is_ti_others_p; 120 | char *ti_others; 121 | 122 | }algoticks_row_ti; 123 | 124 | typedef struct Row 125 | { 126 | /* dataformat to store CSV data */ 127 | char date[32]; 128 | float open; 129 | float high; 130 | float low; 131 | float close; 132 | int volume; 133 | 134 | algoticks_row_ti technical_indicators; 135 | 136 | int curr; 137 | int n_rows; //this is to be set in case of Row array of non-predetermined size. 138 | }algoticks_row; 139 | 140 | 141 | typedef struct PositionResult { 142 | 143 | int n_steps; 144 | 145 | char recv_signal; 146 | char hit_type[4]; 147 | 148 | float pnl; 149 | 150 | int curr; 151 | int eof; 152 | algoticks_row lastrow; 153 | 154 | }algoticks_positionresult; 155 | 156 | 157 | typedef struct SimResult{ 158 | float pnl; 159 | 160 | algoticks_config config; 161 | algoticks_settings settings; 162 | 163 | int buy_signals; 164 | int sell_signals; 165 | int neutral_signals; 166 | 167 | int trgt_hits; 168 | int sl_hits; 169 | 170 | int b_trgt_hits; 171 | int b_sl_hits; 172 | 173 | int s_trgt_hits; 174 | int s_sl_hits; 175 | 176 | float peak; 177 | float bottom; 178 | 179 | }algoticks_simresult; 180 | 181 | 182 | typedef struct Signal { 183 | int buy; 184 | int neutral; 185 | int sell; 186 | }algoticks_signal; 187 | 188 | typedef struct Event { 189 | 190 | int from_sim; 191 | int from_pos; 192 | 193 | algoticks_signal signal; 194 | int t_h; 195 | int sl_h; 196 | 197 | int tsl; /* trailing SL */ 198 | float tsl_t; /* trailing SL target */ 199 | float tsl_sl; /* trailing SL stoploss */ 200 | 201 | char date[32]; 202 | float a; 203 | float b; 204 | float pnl; 205 | 206 | }algoticks_event; 207 | 208 | //function pointers 209 | #if defined(__linux__) || defined(__unix__) || defined(__APPLE__) 210 | typedef algoticks_signal (*algo_func)(algoticks_row *, int); 211 | typedef int (*callback_func)(algoticks_event *); 212 | #endif 213 | 214 | #ifdef _WIN32 215 | typedef algoticks_signal (__cdecl *algo_func)(algoticks_row *, int); 216 | typedef int (__cdecl *callback_func)(algoticks_event *); 217 | #endif 218 | 219 | typedef struct CallbackLoader{ 220 | callback_func callback_func; 221 | void *handle; //this need to be casted according to os 222 | }algoticks_cb_l; 223 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../include/dtypes.h" 7 | #include "../include/debug.h" 8 | #include "../include/misc.h" 9 | #include "../include/parser.h" 10 | 11 | 12 | void debug_msg(bool settings_debug, int settings_debug_level, int msg_debug_level, char *file_name, const char *function, int line_n, char *message){ 13 | 14 | 15 | //check if debug is enabled! 16 | if (settings_debug != true){ 17 | return; 18 | } 19 | 20 | //if debug level less then 0 or greater then 3. 21 | if ( (settings_debug_level <= 0) == true){ 22 | return; 23 | } 24 | 25 | // if less then required debug level. 26 | if (settings_debug_level < msg_debug_level){ 27 | return; 28 | } 29 | 30 | 31 | char *buffer = (char*) malloc(512 * sizeof(char)); 32 | sprintf(buffer, "{\"date\": \"%s %s\", \"file\": \"%s\", \"func\": \"%s\", \"line_n\": %d, \"msg\": \"%s\"}\n", 33 | __DATE__, __TIME__, file_name, function, line_n, message); 34 | 35 | printf("%s", buffer); 36 | free(buffer); 37 | 38 | } 39 | 40 | void debug_msg_simple(char *msg){ 41 | 42 | char *buffer; 43 | buffer = (char*) malloc(sizeof(msg)); 44 | sprintf(buffer, "{\"date\": \"%s %s\", \"msg\": \"%s\"}\n", __DATE__, __TIME__, msg); 45 | printf("%s", buffer); 46 | free(buffer); 47 | 48 | } 49 | 50 | void print_config_struct(algoticks_config *config){ 51 | printf("\n===*===\n"); 52 | printf("[DEBUG] config->algo = %s\n", config->algo); 53 | printf("[DEBUG] config->datasource = %s\n", config->datasource); 54 | printf("[DEBUG] config->derivative[derivative_datasource, derivative_interval] = %s %d\n", config->derivative.derivative_datasource, config->derivative.derivative_interval); 55 | printf("[DEBUG] config->symbol = %s\n", config->symbol); 56 | printf("[DEBUG] config->interval = %d\n", config->interval); 57 | printf("[DEBUG] config->n_callbacks = %d\n", config->n_callbacks); 58 | 59 | if (config->n_callbacks > 0){ 60 | printf("[DEBUG] config->callbacks ="); 61 | for (int i = 0; i < config->n_callbacks; i++) 62 | { 63 | printf(" %s ",config->callbacks[i]); 64 | } 65 | 66 | } 67 | 68 | printf("[DEBUG] config->candles = %d\n", config->candles); 69 | printf("[DEBUG] config->target = %f\n", config->target); 70 | printf("[DEBUG] config->stoploss = %f\n", config->stoploss); 71 | printf("[DEBUG] config->is_trailing_sl = %d\n", config->is_trailing_sl); 72 | printf("[DEBUG] config->trailing_sl_val = %f\n", config->trailing_sl_val); 73 | printf("[DEBUG] config->quantity = %d\n", config->quantity); 74 | 75 | printf("[DEBUG] config->sliding = %d\n", config->sliding); 76 | printf("[DEBUG] config->intraday = %d\n", config->intraday); 77 | printf("[DEBUG] config->skip_header = %d\n", config->skip_header); 78 | 79 | printf("\n===*===\n"); 80 | } 81 | 82 | void print_dashboard_struct(algoticks_dashboard *dashboard){ 83 | printf("\n===*===\n"); 84 | printf("[DEBUG] dashboard->a = %f\n", dashboard->a); 85 | printf("[DEBUG] dashboard->b = %f\n", dashboard->b); 86 | printf("[DEBUG] dashboard->date = %s\n", dashboard->date); 87 | printf("[DEBUG] dashboard->pnl = %f\n", dashboard->pnl); 88 | printf("[DEBUG] dashboard->is_short = %d\n", dashboard->is_short); 89 | printf("\n===*===\n"); 90 | } 91 | 92 | void print_signal_struct(algoticks_signal *signal){ 93 | printf("\n===*===\n"); 94 | printf("[DEBUG] signal->buy = %d\n", signal->buy); 95 | printf("[DEBUG] signal->neutral = %d\n", signal->neutral); 96 | printf("[DEBUG] signal->sell = %d\n", signal->sell); 97 | printf("\n===*===\n"); 98 | } 99 | 100 | void print_simresult_struct(algoticks_simresult *simresult){ 101 | printf("\n===*===\n"); 102 | printf("[DEBUG] signal->pnl = %f\n", simresult->pnl); 103 | printf("[DEBUG] signal->buy_signals = %d\n", simresult->buy_signals); 104 | printf("[DEBUG] signal->sell_signals = %d\n", simresult->sell_signals); 105 | printf("[DEBUG] signal->neutral_signals = %d\n", simresult->neutral_signals); 106 | printf("[DEBUG] signal->trgt_hits = %d\n", simresult->trgt_hits); 107 | printf("[DEBUG] signal->sl_hits = %d\n", simresult->sl_hits); 108 | printf("\n===*===\n"); 109 | } 110 | 111 | void print_row_struct(algoticks_row *row){ 112 | printf("\n===*===\n"); 113 | printf("[DEBUG] row->date = %s\n", row->date); 114 | printf("[DEBUG] row->open = %f\n", row->open); 115 | printf("[DEBUG] row->high = %f\n", row->high); 116 | printf("[DEBUG] row->low = %f\n", row->low); 117 | printf("[DEBUG] row->close = %f\n", row->close); 118 | printf("[DEBUG] row->volume = %d\n", row->volume); 119 | 120 | printf("[DEBUG] ti1 ti2 ti3 t_others = %f %f %f %s\n", row->technical_indicators.ti1, 121 | row->technical_indicators.ti2, 122 | row->technical_indicators.ti3, 123 | row->technical_indicators.ti_others); 124 | printf("\n===*===\n"); 125 | } -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../include/dtypes.h" 6 | #include "../include/sim.h" 7 | #include "../include/sim_derivative.h" 8 | #include "../include/benchmark.h" 9 | #include "../include/parser.h" 10 | #include "../include/misc.h" 11 | #include "../include/debug.h" 12 | 13 | struct program_args 14 | { 15 | int debug; 16 | char *debug_level; 17 | 18 | int benchmark; 19 | 20 | int live_datasource; 21 | int live_datasource_socket; 22 | char *tcp_socket_port; 23 | 24 | int derivative; 25 | 26 | char *settings_f; 27 | char *config_f; 28 | char *benchmark_f; 29 | 30 | /* WINDOWS ONLY */ 31 | char *program_args_f; 32 | }; 33 | 34 | #if defined(__linux__) 35 | #include 36 | 37 | const char *argp_program_version = 38 | "algoticks v2.2"; 39 | const char *argp_program_bug_address = 40 | ""; 41 | 42 | /* Program documentation. */ 43 | static char doc[] = 44 | "Algoticks - algorithmic trading simulator."; 45 | 46 | /* The options we understand. */ 47 | static struct argp_option options[] = { 48 | {"debug", 'D', "LEVEL", 0, "Debug mode."}, 49 | 50 | {"live", 'l', 0, 0, "Live mode. Wait for data at EOF"}, 51 | {"derivative", 'd', 0, 0, "Derivative mode"}, 52 | {"benchmark", 'b', 0, 0, "Benchmark mode"}, 53 | {"socket", 's', "PORT", 0, "Open socket to receive data at EOF"}, 54 | 55 | 56 | {"configfile", 'C', "FILE", 0, "Config file."}, 57 | {"benchmarkfile", 'B', "FILE", 0, "Benchmark file."}, 58 | {"settingsfile", 'S', "FILE", 0, "settings file."}, 59 | {0} 60 | }; 61 | 62 | static error_t parse_opt(int key, char *arg, struct argp_state *state) 63 | { 64 | /* Get the input argument from argp_parse, which we 65 | know is a pointer to our arguments structure. */ 66 | struct program_args *arguments = state->input; 67 | 68 | switch (key) 69 | { 70 | case 'D': 71 | arguments->debug = 1; 72 | arguments->debug_level = arg; 73 | break; 74 | case 'l': 75 | arguments->live_datasource = 1; 76 | break; 77 | 78 | case 'd': 79 | arguments->derivative = 1; 80 | break; 81 | 82 | case 'b': 83 | arguments->benchmark = 1; 84 | break; 85 | 86 | case 's': 87 | arguments->live_datasource_socket = 1; 88 | arguments->tcp_socket_port = arg; 89 | break; 90 | 91 | case 'C': 92 | arguments->config_f = arg; 93 | break; 94 | 95 | case 'B': 96 | arguments->benchmark_f = arg; 97 | break; 98 | 99 | case 'S': 100 | arguments->settings_f = arg; 101 | break; 102 | 103 | default: 104 | return ARGP_ERR_UNKNOWN; 105 | } 106 | return 0; 107 | } 108 | 109 | static struct argp argp = { options, parse_opt, 0, doc }; 110 | #endif 111 | 112 | int main(int argc, char **argv) 113 | { 114 | 115 | struct program_args arguments = {0}; 116 | 117 | /* Defaults */ 118 | arguments.config_f = "config.json"; 119 | arguments.benchmark_f = "benchmark.json"; 120 | arguments.settings_f = "settings.json"; 121 | arguments.tcp_socket_port = "6060"; 122 | arguments.debug = false; 123 | arguments.debug_level = 0; 124 | 125 | #if defined(__linux__) 126 | argp_parse (&argp, argc, argv, 0, 0, &arguments); 127 | #endif 128 | 129 | //in windows, we only take in single settings file as arg. 130 | #ifdef _WIN32 131 | for (size_t i = 0; i < argc; i++) 132 | { 133 | if(strstr(argv[i], ".json") != NULL){ 134 | arguments.settings_f = argv[i]; 135 | break; 136 | } 137 | } 138 | 139 | #endif 140 | 141 | struct Config config; 142 | struct Settings settings; 143 | 144 | settings = parse_settings_from_json(arguments.settings_f); 145 | 146 | #ifdef _WIN32 147 | arguments.config_f = settings.config_f; 148 | arguments.benchmark_f = settings.benchmark_f; 149 | #endif 150 | 151 | // mutually exclusive ways to feed data. 152 | if (settings.is_live_data && settings.is_live_data_socket){ 153 | settings.is_live_data_socket = false; 154 | } 155 | 156 | #if defined(__linux__) 157 | if (arguments.debug) { 158 | settings.debug = true; 159 | settings.debug_level = atoi(arguments.debug_level); 160 | } 161 | 162 | if (arguments.derivative){ 163 | settings.is_derivative = true; 164 | } 165 | 166 | if (arguments.live_datasource){ 167 | settings.is_live_data = true; 168 | } 169 | 170 | if (arguments.live_datasource_socket){ 171 | settings.is_live_data_socket = true; 172 | settings.socket_port = (char*) malloc(5 * sizeof(char)); 173 | strncpy(settings.socket_port, arguments.tcp_socket_port, 5); 174 | } 175 | #endif 176 | 177 | config = parse_config_from_json(arguments.config_f); 178 | 179 | if (arguments.benchmark || settings.is_benchmark){ 180 | run_benchmark(arguments.benchmark_f, settings); 181 | } 182 | else if(arguments.derivative || settings.is_derivative){ 183 | run_sim_w_derivative(&settings, &config); 184 | } 185 | else{ 186 | run_sim(&settings, &config); 187 | } 188 | 189 | free_algoticks_config(&config); 190 | free_algoticks_settings(&settings); 191 | 192 | return 0; 193 | } -------------------------------------------------------------------------------- /src/timeutils.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../include/dtypes.h" 7 | #include "../include/csvutils.h" 8 | #include "../include/timeutils.h" 9 | #include "../include/debug.h" 10 | 11 | char debug_buffer[512]; 12 | const char *strp_format_1 = "%Y-%m-%d %H:%M:%S"; 13 | const char *scanf_format_notime = "%4d-%2d-%2d"; 14 | const char *scanf_time_format_1 = "%4d-%2d-%2d %2d:%2d:%2d"; 15 | 16 | const char *strp_format_2 = "%Y/%m/%d %H:%M:%S"; 17 | const char *scanf_format2_notime = "%4d/%2d/%2d"; 18 | const char *scanf_time_format_2 = "%4d/%2d/%2d %2d:%2d:%2d"; 19 | 20 | int is_date_over_or_eq_intraday(char *date, int intraday_hour, int intraday_min) 21 | { 22 | 23 | struct tm date_ts = {0}; 24 | 25 | #ifdef _WIN32 26 | if (!get_time_with_sscanf_from_string(date, &date_ts)) 27 | { 28 | return -1; 29 | } 30 | 31 | #else 32 | if (!strptime(date, strp_format_1, &date_ts)) 33 | { 34 | if (!strptime(date, strp_format_2, &date_ts)) 35 | { 36 | return -1; 37 | } 38 | } 39 | #endif 40 | 41 | 42 | date_ts.tm_year += 1900; 43 | 44 | if ((date_ts.tm_hour >= intraday_hour) == true && (date_ts.tm_min >= intraday_min) == true) 45 | { 46 | return true; 47 | } 48 | else 49 | { 50 | return false; 51 | } 52 | } 53 | 54 | int is_date_after(char *date_a, char *date_b) 55 | { 56 | 57 | struct tm date_a_ts = {0}; 58 | struct tm date_b_ts = {0}; 59 | 60 | time_t x; 61 | time_t y; 62 | 63 | double diff; 64 | 65 | if(!get_time_with_sscanf_from_string(date_a, &date_a_ts)){ 66 | return -1; 67 | } 68 | 69 | if(!get_time_with_sscanf_from_string(date_b, &date_b_ts)){ 70 | return -1; 71 | } 72 | 73 | x = mktime(&date_a_ts); 74 | y = mktime(&date_b_ts); 75 | 76 | diff = difftime(x, y); 77 | 78 | if (diff >= 0){ 79 | return true; 80 | } 81 | 82 | return false; 83 | } 84 | 85 | int is_date_before(char *date_a, char *date_b) 86 | { 87 | 88 | struct tm date_a_ts = {0}; 89 | struct tm date_b_ts = {0}; 90 | 91 | time_t x; 92 | time_t y; 93 | 94 | if(!get_time_with_sscanf_from_string(date_a, &date_a_ts)){ 95 | return -1; 96 | } 97 | 98 | if(!get_time_with_sscanf_from_string(date_b, &date_b_ts)){ 99 | return -1; 100 | } 101 | 102 | 103 | x = mktime(&date_a_ts); 104 | y = mktime(&date_b_ts); 105 | 106 | double diff = difftime(x,y); 107 | 108 | if (diff <= 0){ 109 | return true; 110 | } 111 | 112 | return false; 113 | 114 | } 115 | 116 | int get_time_with_sscanf_from_string(char *date, struct tm *time_struct){ 117 | 118 | int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0; 119 | 120 | if ( sscanf(date, scanf_time_format_1, &year, &month, &day, &hour, &min, &sec) == 6 ) { 121 | time_struct->tm_year = year - 1900; 122 | time_struct->tm_mon = month - 1; 123 | time_struct->tm_mday = day; 124 | time_struct->tm_hour = hour; 125 | time_struct->tm_min = min; 126 | time_struct->tm_sec = sec; 127 | 128 | return true; 129 | } 130 | else if ( sscanf(date, scanf_time_format_2, &year, &month, &day, &hour, &min, &sec) == 6 ) { 131 | 132 | time_struct->tm_year = year - 1900; 133 | time_struct->tm_mon = month - 1; 134 | time_struct->tm_mday = day; 135 | time_struct->tm_hour = hour; 136 | time_struct->tm_min = min; 137 | time_struct->tm_sec = sec; 138 | 139 | return true; 140 | } 141 | 142 | else if ( sscanf(date, scanf_format_notime, &year, &month, &day) == 3 ) { 143 | 144 | time_struct->tm_year = year - 1900; 145 | time_struct->tm_mon = month - 1; 146 | time_struct->tm_mday = day; 147 | time_struct->tm_hour = hour; 148 | time_struct->tm_min = min; 149 | time_struct->tm_sec = sec; 150 | 151 | return true; 152 | } 153 | 154 | else if ( sscanf(date, scanf_format2_notime, &year, &month, &day) == 3 ) { 155 | 156 | time_struct->tm_year = year - 1900; 157 | time_struct->tm_mon = month - 1; 158 | time_struct->tm_mday = day; 159 | time_struct->tm_hour = hour; 160 | time_struct->tm_min = min; 161 | time_struct->tm_sec = sec; 162 | 163 | return true; 164 | } 165 | else{ 166 | return false; 167 | } 168 | 169 | } 170 | 171 | int sync_curr(algoticks_settings *settings, algoticks_config *config, FILE *f, char* fname, char *date, int seek_offset, int debug){ 172 | 173 | algoticks_config sync_config = {0}; 174 | int curr = seek_offset; 175 | 176 | sprintf(debug_buffer, "finding %s in %s\n", date, fname); 177 | debug_msg(settings->debug, settings->debug_level, 3, __FILE__, __FUNCTION__, __LINE__, debug_buffer); 178 | 179 | while(curr != EOF || curr != -1){ 180 | struct Row r; 181 | curr = read_csv(settings, &sync_config, f, fname, &r, curr); 182 | sprintf(debug_buffer, "is %s > %s : %d", r.date, date, is_date_after(r.date, date)); 183 | debug_msg(settings->debug, settings->debug_level, 4, __FILE__, __FUNCTION__, __LINE__, debug_buffer); 184 | int res = is_date_after(r.date, date); 185 | 186 | if (res == true){ 187 | return curr; 188 | } 189 | else if (res == -1){ 190 | break; 191 | } 192 | } 193 | 194 | //if nothing, return -1 indicating error. 195 | return -1; 196 | } -------------------------------------------------------------------------------- /src/benchmark.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../include/dtypes.h" 6 | #include "../include/misc.h" 7 | #include "../include/benchmark.h" 8 | #include "../include/debug.h" 9 | #include "../include/sim.h" 10 | #include "../include/sim_derivative.h" 11 | #include "../include/parser.h" 12 | 13 | void run_benchmark(char *benchmark_config_file, algoticks_settings settings) 14 | { 15 | 16 | 17 | 18 | //disable printing 19 | settings.print = false; 20 | settings.is_live_data = false; 21 | settings.is_live_data_socket = false; 22 | struct BenchmarkConfig benchmarkconfig; 23 | benchmarkconfig = parse_benchmark_from_json(benchmark_config_file); 24 | 25 | 26 | //total 11 fields in config = 11 loops 27 | 28 | int total_combinations = 1; 29 | total_combinations *= benchmarkconfig.n_algo; 30 | total_combinations *= benchmarkconfig.n_datasource; 31 | total_combinations *= benchmarkconfig.n_candles; 32 | total_combinations *= benchmarkconfig.n_interval; 33 | 34 | total_combinations *= benchmarkconfig.n_target; 35 | total_combinations *= benchmarkconfig.n_stoploss; 36 | total_combinations *= benchmarkconfig.n_is_trailing_sl; 37 | total_combinations *= benchmarkconfig.n_trailing_sl_val; 38 | total_combinations *= benchmarkconfig.n_quantity; 39 | total_combinations *= benchmarkconfig.n_sliding; 40 | total_combinations *= benchmarkconfig.n_intraday; 41 | 42 | printf("Total Unique Combinations: %d\n", total_combinations); 43 | 44 | int combination_completed = 1; 45 | float progress = 0; 46 | size_t len; 47 | 48 | for (int n_algo = 0; n_algo < benchmarkconfig.n_algo; n_algo++) 49 | { 50 | for (int n_datasource = 0; n_datasource < benchmarkconfig.n_datasource; n_datasource++) 51 | { 52 | for (int n_candles = 0; n_candles < benchmarkconfig.n_candles; n_candles++) 53 | { 54 | for (int n_interval = 0; n_interval < benchmarkconfig.n_interval; n_interval++) 55 | { 56 | for (int n_target = 0; n_target < benchmarkconfig.n_target; n_target++) 57 | { 58 | for (int n_stoploss = 0; n_stoploss < benchmarkconfig.n_stoploss; n_stoploss++) 59 | { 60 | for (int n_is_training_sl = 0; n_is_training_sl < benchmarkconfig.n_is_trailing_sl; n_is_training_sl++) 61 | { 62 | for (int n_trailing_sl_val = 0; n_trailing_sl_val < benchmarkconfig.n_trailing_sl_val; n_trailing_sl_val++) 63 | { 64 | for (int n_quantity = 0; n_quantity < benchmarkconfig.n_quantity; n_quantity++) 65 | { 66 | for (int n_sliding = 0; n_sliding < benchmarkconfig.n_sliding; n_sliding++) 67 | { 68 | for (int n_intraday = 0; n_intraday < benchmarkconfig.n_intraday; n_intraday++) 69 | { 70 | // make a new config struct 71 | algoticks_config config = {0}; 72 | algoticks_derivative derivative = {0}; 73 | 74 | len = strlen(benchmarkconfig.algo_arr[n_algo]); 75 | config.algo = (char*) malloc((len+1) * sizeof(char)); 76 | strcpy(config.algo, benchmarkconfig.algo_arr[n_algo]); 77 | 78 | len = strlen(benchmarkconfig.datasource_arr[n_datasource]); 79 | config.datasource = (char*) malloc((len+1) * sizeof(char)); 80 | strcpy(config.datasource, benchmarkconfig.datasource_arr[n_datasource]); 81 | 82 | len = strlen(benchmarkconfig.symbol); 83 | config.symbol = (char*) malloc((len+1) * sizeof(char)); 84 | strcpy(config.symbol, benchmarkconfig.symbol); 85 | 86 | if (settings.is_derivative){ 87 | derivative.derivative_datasource = (char*) malloc((strlen(benchmarkconfig.derivative.derivative_datasource) + 1) * sizeof(char)); 88 | strcpy(derivative.derivative_datasource, benchmarkconfig.derivative.derivative_datasource); 89 | derivative.derivative_interval = benchmarkconfig.derivative.derivative_interval; 90 | } 91 | 92 | config.derivative = derivative; 93 | 94 | config.candles = benchmarkconfig.candles_arr[n_candles]; 95 | config.interval = benchmarkconfig.interval_arr[n_interval]; 96 | 97 | config.target = benchmarkconfig.target_arr[n_target]; 98 | config.stoploss = benchmarkconfig.stoploss_arr[n_stoploss]; 99 | config.is_trailing_sl = benchmarkconfig.is_trailing_sl[n_is_training_sl]; 100 | config.trailing_sl_val = benchmarkconfig.trailing_sl_val_arr[n_trailing_sl_val]; 101 | config.quantity = benchmarkconfig.quantity_arr[n_quantity]; 102 | config.n_callbacks = 0; //not supported in benchmark 103 | config.sliding = benchmarkconfig.sliding[n_sliding]; 104 | config.intraday = benchmarkconfig.intraday[n_intraday]; 105 | 106 | config.skip_header = benchmarkconfig.skip_header; 107 | 108 | //print config combination to user if debug is enabled and debug level greater then 3. 109 | if (settings.debug == true && (settings.debug_level >= 4) == true){ 110 | print_config_struct(&config); 111 | } 112 | 113 | if (settings.is_derivative){ 114 | run_sim_w_derivative(&settings, &config); 115 | } 116 | else{ 117 | run_sim(&settings, &config); 118 | } 119 | free_algoticks_config(&config); 120 | 121 | combination_completed++; 122 | 123 | progress = ((float)combination_completed / total_combinations) * 100; 124 | printf("%d/%d (%f%%)\r", combination_completed, total_combinations, progress); 125 | fflush(stdout); 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | free_algoticks_benchmark(&benchmarkconfig); 139 | 140 | } -------------------------------------------------------------------------------- /tests/algoticks_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../include/dtypes.h" 7 | 8 | #include "../include/debug.h" 9 | #include "../include/misc.h" 10 | #include "../include/timeutils.h" 11 | #include "../include/csvutils.h" 12 | #include "../include/parser.h" 13 | #include "../include/dashboard.h" 14 | 15 | /* 16 | 17 | Tests for: 18 | 19 | 20 | 21 | from misc.c: 22 | void write_simresult_to_csv(algoticks_simresult simresult); 23 | algoticks_config filter_boundaries(algoticks_config config, int is_short); 24 | int is_target_hit(algoticks_dashboard dashboard, float target); 25 | int is_stoploss_hit(algoticks_dashboard dashboard, float stoploss); 26 | void convert_to_lowercase(char *str); 27 | void remove_quotes(char *str); 28 | void chomp(char *s); 29 | 30 | from dashboard.c: 31 | float getPnL(algoticks_dashboard dashboard) 32 | 33 | from timeutils.c: 34 | int is_date_over_or_eq_intraday(char *date, int intraday_hour, int intraday_min); 35 | int is_date_after(char *date_a, char *date_b); 36 | int get_time_with_sscanf_from_string(char* date, struct tm *time_struct); 37 | 38 | from csvutls.c: 39 | 40 | int is_quoted(char *str); 41 | */ 42 | 43 | 44 | START_TEST 45 | (test_it_works_2_plus_2) { 46 | 47 | ck_assert_int_eq((2+2), 4); 48 | 49 | } 50 | END_TEST 51 | 52 | 53 | START_TEST 54 | (__misc_c__write_simresult_to_csv) { 55 | 56 | algoticks_simresult sr = {0}; 57 | write_simresult_to_csv(&sr); 58 | int res = is_file_exists("results.csv"); 59 | 60 | ck_assert_int_eq(res, true); 61 | } 62 | END_TEST 63 | 64 | 65 | START_TEST 66 | (__misc_c__filter_boundaries) { 67 | 68 | algoticks_config x = parse_config_from_json("config.json"); 69 | 70 | x.target = 15; 71 | x.stoploss = 20; 72 | 73 | filter_boundaries(&x, false); 74 | ck_assert_int_eq(x.target, 15); 75 | ck_assert_int_eq(x.stoploss, -20); 76 | 77 | filter_boundaries(&x,true); 78 | ck_assert_int_eq(x.target, -15); 79 | ck_assert_int_eq(x.stoploss, 20); 80 | 81 | } 82 | END_TEST 83 | 84 | START_TEST 85 | (__misc_c__is_target_hit) { 86 | 87 | algoticks_dashboard d; 88 | 89 | d.a = 10; 90 | d.b = 15; 91 | d.is_short = false; 92 | 93 | // NOTE: target and stoploss are abs. vals. 94 | 95 | ck_assert_int_eq(is_target_hit(d, 3), true); 96 | ck_assert_int_eq(is_target_hit(d, 7), false); 97 | 98 | 99 | d.a = 10; 100 | d.b = 7; 101 | d.is_short = true; 102 | 103 | ck_assert_int_eq(is_target_hit(d, -2), true); 104 | ck_assert_int_eq(is_target_hit(d, -8), false); 105 | 106 | } 107 | END_TEST 108 | 109 | START_TEST 110 | (__misc_c__is_stoploss_hit) { 111 | 112 | algoticks_dashboard d; 113 | 114 | d.a = 10; 115 | d.b = 15; 116 | d.is_short = false; 117 | 118 | // NOTE: target and stoploss are abs. vals. 119 | 120 | ck_assert_int_eq(is_stoploss_hit(d, 3), false); 121 | ck_assert_int_eq(is_stoploss_hit(d, 7), true); 122 | 123 | 124 | d.a = 10; 125 | d.b = 7; 126 | d.is_short = true; 127 | 128 | ck_assert_int_eq(is_stoploss_hit(d, -2), false); 129 | ck_assert_int_eq(is_stoploss_hit(d, -8), true); 130 | 131 | } 132 | END_TEST 133 | 134 | 135 | 136 | START_TEST 137 | (__misc_c__convert_to_lowercase) { 138 | 139 | char str[12] = "TeStStRiNg"; 140 | convert_to_lowercase(str); 141 | 142 | ck_assert_str_eq(str, "teststring"); 143 | 144 | } 145 | END_TEST 146 | 147 | 148 | START_TEST 149 | (__misc_c__remove_quotes) { 150 | 151 | char str[7] = "\"test\""; 152 | remove_quotes(str); 153 | ck_assert_str_eq(str, "test"); 154 | 155 | } 156 | END_TEST 157 | 158 | 159 | 160 | START_TEST 161 | (__misc_c__chomp) { 162 | 163 | char str[7] = "test\n"; 164 | chomp(str); 165 | ck_assert_str_eq(str, "test"); 166 | 167 | } 168 | END_TEST 169 | 170 | 171 | START_TEST 172 | (__dashboard_c__getPnL) { 173 | 174 | algoticks_dashboard d; 175 | d.a = 10; 176 | d.b = 15; 177 | d.q = 100; 178 | d.is_short = false; 179 | 180 | ck_assert_float_eq(getPnL(d), (5*100)); 181 | 182 | d.a = 10; 183 | d.b = 8; 184 | d.q = 100; 185 | d.is_short = false; 186 | 187 | ck_assert_float_eq(getPnL(d), (-2*100)); 188 | 189 | d.a = 10; 190 | d.b = 8; 191 | d.q = 100; 192 | d.is_short = true; 193 | 194 | ck_assert_float_eq(getPnL(d), (2*100)); 195 | 196 | d.a = 10; 197 | d.b = 12; 198 | d.q = 100; 199 | d.is_short = true; 200 | 201 | ck_assert_float_eq(getPnL(d), (-2*100)); 202 | 203 | } 204 | END_TEST 205 | 206 | START_TEST 207 | (__timeutils_c__is_date_over_or_eq_intraday) { 208 | 209 | ck_assert_int_eq(is_date_over_or_eq_intraday("2015-02-02 09:25:00", 15, 15), false); 210 | ck_assert_int_eq(is_date_over_or_eq_intraday("2015-02-02 15:29:00", 15, 15), true); 211 | 212 | } 213 | END_TEST 214 | 215 | 216 | START_TEST 217 | (__timeutils_c__is_date_after) { 218 | 219 | //is a > b 220 | ck_assert_int_eq(is_date_after("2019-02-02 09:25:00", "2015-02-02 09:20:00"), true); 221 | ck_assert_int_eq(is_date_after("2014-02-02 09:25:00", "2015-02-02 09:20:00"), false); 222 | 223 | ck_assert_int_eq(is_date_before("2014-01-01 09:25:00", "2015-01-01 09:00:00"), true); 224 | ck_assert_int_eq(is_date_before("2013-01-01 09:25:00", "2010-01-01 09:00:00"), false); 225 | 226 | } 227 | END_TEST 228 | 229 | 230 | 231 | START_TEST 232 | (__timeutils_c__get_time_with_sscanf_from_string) { 233 | 234 | struct tm time_test; 235 | 236 | get_time_with_sscanf_from_string("2015-02-02 09:25:51", &time_test); 237 | 238 | ck_assert_int_eq(time_test.tm_year, 2015 - 1900); 239 | ck_assert_int_eq(time_test.tm_mon, 2 - 1); 240 | ck_assert_int_eq(time_test.tm_mday, 2); 241 | 242 | ck_assert_int_eq(time_test.tm_hour, 9); 243 | ck_assert_int_eq(time_test.tm_min, 25); 244 | ck_assert_int_eq(time_test.tm_sec, 51); 245 | 246 | } 247 | END_TEST 248 | 249 | 250 | START_TEST 251 | (__csvutils_c__is_quoted) { 252 | 253 | char str[10] = "\"test"; 254 | char str2[10] = "test"; 255 | 256 | ck_assert_int_eq(is_quoted(str), true); 257 | ck_assert_int_eq(is_quoted(str2), false); 258 | 259 | } 260 | END_TEST 261 | 262 | START_TEST(__csvutils_c__parse_header_DOHLC) 263 | { 264 | 265 | algoticks_config config = parse_config_from_json("config.json"); 266 | algoticks_settings settings = parse_settings_from_json("settings.json"); 267 | config.skip_header = false; 268 | reset_header_skip(); 269 | 270 | algoticks_row row = {0}; 271 | 272 | FILE *f = fopen("example_DOHLC.csv", "r"); 273 | 274 | read_csv(&settings, &config, f, "example_DOHLC.csv", &row, 0); 275 | ck_assert_int_eq(is_mapped(0, "date"), true); 276 | ck_assert_int_eq(is_mapped(1, "open"), true); 277 | ck_assert_int_eq(is_mapped(2, "high"), true); 278 | ck_assert_int_eq(is_mapped(3, "low"), true); 279 | ck_assert_int_eq(is_mapped(4, "close"), true); 280 | } 281 | END_TEST 282 | 283 | START_TEST(__csvutils_c__parse_header_OCDHL) 284 | { 285 | 286 | algoticks_config config = parse_config_from_json("config.json"); 287 | algoticks_settings settings = parse_settings_from_json("settings.json"); 288 | config.skip_header = false; 289 | reset_header_skip(); 290 | 291 | algoticks_row row = {0}; 292 | 293 | FILE *f = fopen("example_OCDHL.csv", "r"); 294 | 295 | read_csv(&settings, &config, f, "example_OCDHL.csv", &row, 0); 296 | ck_assert_int_eq(is_mapped(0, "open"), true); 297 | ck_assert_int_eq(is_mapped(1, "close"), true); 298 | ck_assert_int_eq(is_mapped(2, "date"), true); 299 | ck_assert_int_eq(is_mapped(3, "high"), true); 300 | ck_assert_int_eq(is_mapped(4, "low"), true); 301 | } 302 | END_TEST 303 | 304 | START_TEST(__csvutils_c__parse_header_ODHLC) 305 | { 306 | 307 | algoticks_config config = parse_config_from_json("config.json"); 308 | algoticks_settings settings = parse_settings_from_json("settings.json"); 309 | config.skip_header = false; 310 | reset_header_skip(); 311 | 312 | algoticks_row row = {0}; 313 | 314 | FILE *f = fopen("example_ODHLC.csv", "r"); 315 | 316 | read_csv(&settings, &config, f, "example_ODHLC.csv", &row, 0); 317 | ck_assert_int_eq(is_mapped(0, "open"), true); 318 | ck_assert_int_eq(is_mapped(1, "date"), true); 319 | ck_assert_int_eq(is_mapped(2, "high"), true); 320 | ck_assert_int_eq(is_mapped(3, "low"), true); 321 | ck_assert_int_eq(is_mapped(4, "close"), true); 322 | } 323 | END_TEST 324 | 325 | Suite *algoticks_suite(void) { 326 | Suite *s; 327 | TCase *tc_core; 328 | 329 | s = suite_create("algoticks"); 330 | tc_core = tcase_create("Core"); 331 | 332 | tcase_add_test(tc_core, test_it_works_2_plus_2); 333 | tcase_add_test(tc_core, __misc_c__write_simresult_to_csv); 334 | tcase_add_test(tc_core, __misc_c__filter_boundaries); 335 | tcase_add_test(tc_core, __misc_c__is_target_hit); 336 | tcase_add_test(tc_core, __misc_c__is_stoploss_hit); 337 | tcase_add_test(tc_core, __misc_c__convert_to_lowercase); 338 | tcase_add_test(tc_core, __misc_c__remove_quotes); 339 | tcase_add_test(tc_core, __misc_c__chomp); 340 | tcase_add_test(tc_core, __dashboard_c__getPnL); 341 | 342 | tcase_add_test(tc_core, __timeutils_c__is_date_after); 343 | tcase_add_test(tc_core, __timeutils_c__is_date_over_or_eq_intraday); 344 | tcase_add_test(tc_core, __timeutils_c__get_time_with_sscanf_from_string); 345 | 346 | tcase_add_test(tc_core, __csvutils_c__is_quoted); 347 | tcase_add_test(tc_core, __csvutils_c__parse_header_DOHLC); 348 | tcase_add_test(tc_core, __csvutils_c__parse_header_OCDHL); 349 | tcase_add_test(tc_core, __csvutils_c__parse_header_ODHLC); 350 | 351 | suite_add_tcase(s, tc_core); 352 | 353 | return s; 354 | } 355 | 356 | int main(void) { 357 | int no_failed = 0; 358 | Suite *s; 359 | SRunner *runner; 360 | 361 | s = algoticks_suite(); 362 | runner = srunner_create(s); 363 | 364 | srunner_run_all(runner, CK_NORMAL); 365 | no_failed = srunner_ntests_failed(runner); 366 | srunner_free(runner); 367 | return (no_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 368 | } -------------------------------------------------------------------------------- /src/misc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "../include/dtypes.h" 9 | #include "../include/debug.h" 10 | #include "../include/misc.h" 11 | 12 | #if defined(__linux__) || defined(__unix__) || defined(__APPLE__) 13 | #include 14 | 15 | algoticks_cb_l load_cb(char *cb){ 16 | 17 | void* handle; 18 | callback_func callback; 19 | 20 | printf("%s\n", cb); 21 | 22 | handle = dlopen(cb, RTLD_LAZY); 23 | 24 | if (!handle) 25 | { 26 | fprintf(stderr, "Error: %s\n", dlerror()); 27 | exit(EXIT_FAILURE); 28 | } 29 | 30 | *(void **)(&callback) = dlsym(handle, "callback_f"); 31 | 32 | if (!callback) 33 | { 34 | fprintf(stderr, "Error: %s\n", dlerror()); 35 | dlclose(handle); 36 | exit(EXIT_FAILURE); 37 | } 38 | 39 | algoticks_cb_l loader; 40 | loader.callback_func = callback; 41 | loader.handle = handle; 42 | 43 | return loader; 44 | 45 | } 46 | 47 | //this is for algo loading 48 | void* handle; 49 | 50 | algo_func load_algo_func(char *algo){ 51 | 52 | algoticks_signal (*analyze)(algoticks_row *, int); 53 | 54 | handle = dlopen(algo, RTLD_LAZY); 55 | 56 | if (!handle) 57 | { 58 | fprintf(stderr, "Error: %s\n", dlerror()); 59 | exit(EXIT_FAILURE); 60 | } 61 | 62 | *(void **)(&analyze) = dlsym(handle, "analyze"); 63 | 64 | if (!analyze) 65 | { 66 | fprintf(stderr, "Error: %s\n", dlerror()); 67 | dlclose(handle); 68 | exit(EXIT_FAILURE); 69 | } 70 | 71 | return analyze; 72 | 73 | } 74 | 75 | void close_algo_func(){ 76 | 77 | int cr = dlclose(handle); 78 | 79 | if (cr != 0){ 80 | printf("cannot close algo_func\n"); 81 | } 82 | 83 | } 84 | #endif 85 | 86 | #ifdef _WIN32 87 | #include 88 | 89 | algoticks_cb_l load_cb(char *algo) { 90 | 91 | HINSTANCE hinstLib; 92 | callback_func ProcAdd; 93 | 94 | // Get a handle to the DLL module. 95 | hinstLib = LoadLibrary(algo); 96 | 97 | // If the handle is valid, try to get the function address. 98 | if (hinstLib != NULL) { 99 | ProcAdd = (algo_func) GetProcAddress(hinstLib, "callback_f"); 100 | 101 | // If the function address is valid, call the function. 102 | if (NULL != ProcAdd) { 103 | algoticks_cb_l l; 104 | l.callback_func = ProcAdd; 105 | l.handle = hinstLib; 106 | 107 | return l; 108 | 109 | } else { 110 | printf("cannot open %s\n", algo); 111 | exit(1); 112 | 113 | } 114 | 115 | } 116 | else{ 117 | printf("cannot get handle on %s\n", algo); 118 | exit(1); 119 | } 120 | 121 | } 122 | 123 | 124 | HINSTANCE hinstLib; 125 | 126 | algo_func load_algo_func(char *algo) { 127 | 128 | algo_func ProcAdd; 129 | 130 | // Get a handle to the DLL module. 131 | hinstLib = LoadLibrary(algo); 132 | 133 | // If the handle is valid, try to get the function address. 134 | if (hinstLib != NULL) { 135 | ProcAdd = (algo_func) GetProcAddress(hinstLib, "analyze"); 136 | 137 | // If the function address is valid, call the function. 138 | if (NULL != ProcAdd) { 139 | return ProcAdd; 140 | 141 | } else { 142 | printf("cannot open %s\n", algo); 143 | exit(1); 144 | 145 | } 146 | 147 | } 148 | else{ 149 | printf("cannot get handle on %s\n", algo); 150 | exit(1); 151 | } 152 | 153 | } 154 | 155 | void close_algo_func() { 156 | FreeLibrary(hinstLib); 157 | } 158 | 159 | # endif 160 | 161 | 162 | // global vars for write_simresult_to_csv 163 | int simresult_file_header = false; 164 | char simresult_csv_header[1000] = "algo,pnl,datasource,derivative,symbol,candles,interval,target,stoploss,is_trailing_sl,trailing_sl_val,quantity,sliding,intraday,buy_signals,sell_signals,neutral_signals,trgt_hits,sl_hits,b_trgt_hits,s_trgt_hits,b_sl_hits,s_sl_hits,peak,bottom\n"; 165 | 166 | void write_simresult_to_csv(algoticks_simresult *simresult) 167 | { 168 | // config + sim result 169 | char *buffer = (char*) malloc(4000 * sizeof(char)); 170 | 171 | sprintf(buffer, "%s,%f,%s,%s,%s,%d,%d,%f,%f,%d,%f,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%f,%f\n", 172 | simresult->config.algo, 173 | simresult->pnl, 174 | simresult->config.datasource, 175 | simresult->config.derivative.derivative_datasource, 176 | simresult->config.symbol, 177 | simresult->config.candles, 178 | simresult->config.interval, 179 | simresult->config.target, 180 | simresult->config.stoploss, 181 | simresult->config.is_trailing_sl, 182 | simresult->config.trailing_sl_val, 183 | simresult->config.quantity, 184 | simresult->config.sliding, 185 | simresult->config.intraday, 186 | simresult->buy_signals, 187 | simresult->sell_signals, 188 | simresult->neutral_signals, 189 | simresult->trgt_hits, 190 | simresult->sl_hits, 191 | simresult->b_trgt_hits, 192 | simresult->s_trgt_hits, 193 | simresult->b_sl_hits, 194 | simresult->s_sl_hits, 195 | simresult->peak, 196 | simresult->bottom); 197 | FILE *fp; 198 | if (simresult_file_header == false) 199 | { 200 | fp = fopen("results.csv", "w+"); 201 | fprintf(fp, simresult_csv_header); 202 | fclose(fp); 203 | 204 | simresult_file_header = true; 205 | } 206 | 207 | fp = fopen("results.csv", "a"); 208 | fprintf(fp, buffer); 209 | free(buffer); 210 | fclose(fp); 211 | 212 | } 213 | 214 | int is_file_exists(const char *filename) 215 | { 216 | /* try to open file to read */ 217 | FILE *file; 218 | file = fopen(filename, "r"); 219 | if (file != NULL) 220 | { 221 | fclose(file); 222 | return 1; 223 | } 224 | return 0; 225 | } 226 | 227 | void filter_boundaries(algoticks_config *config, int is_short) 228 | { 229 | 230 | if (is_short == true) 231 | { 232 | 233 | config->target = -(fabs(config->target)); 234 | config->stoploss = fabs(config->stoploss); 235 | config->trailing_sl_val = -(fabs(config->trailing_sl_val)); 236 | } 237 | else 238 | { 239 | 240 | config->target = fabs(config->target); 241 | config->stoploss = -(fabs(config->stoploss)); 242 | config->trailing_sl_val = fabs(config->trailing_sl_val); 243 | } 244 | } 245 | 246 | //essential boundary checking fuctions 247 | int is_target_hit(algoticks_dashboard dashboard, float target) 248 | { 249 | if (dashboard.is_short == true) 250 | { 251 | if ((dashboard.b - dashboard.a) < target) 252 | { 253 | return true; 254 | } 255 | else 256 | { 257 | return false; 258 | } 259 | } 260 | else 261 | { 262 | if ((dashboard.b - dashboard.a) >= target) 263 | { 264 | return true; 265 | } 266 | else 267 | { 268 | return false; 269 | } 270 | } 271 | } 272 | 273 | int is_stoploss_hit(algoticks_dashboard dashboard, float stoploss) 274 | { 275 | if (dashboard.is_short == true) 276 | { 277 | // sell / short 278 | if ((dashboard.b - dashboard.a) > stoploss) 279 | { 280 | return true; 281 | } 282 | else 283 | { 284 | //printf("SL HIT => Received: %f %f %d\n", abs_pnl, stoploss, is_short); 285 | return false; 286 | } 287 | } 288 | else 289 | { 290 | // buy 291 | if ((dashboard.b - dashboard.a) <= stoploss) 292 | { 293 | return true; 294 | } 295 | else 296 | { 297 | return false; 298 | } 299 | } 300 | } 301 | 302 | /* TODO - brokerage calculator */ 303 | float brokerage_calc() 304 | { 305 | 306 | return 1.0; 307 | } 308 | 309 | 310 | void convert_to_lowercase(char *str){ 311 | for (int i = 0; i < strlen(str); i++) 312 | { 313 | str[i] = tolower(str[i]); 314 | } 315 | 316 | } 317 | 318 | void remove_quotes(char *str){ 319 | int i; 320 | 321 | for (i = 1; i < strlen(str)-1; i++) 322 | { 323 | str[i-1] = str[i]; 324 | } 325 | str[i-1] = '\0'; 326 | } 327 | 328 | void chomp(char *s) 329 | { 330 | /* This removes newline at end of string */ 331 | while (*s && *s != '\n' && *s != '\r') 332 | s++; 333 | *s = 0; 334 | } 335 | 336 | void free_algoticks_config(algoticks_config *config){ 337 | free(config->algo); 338 | free(config->datasource); 339 | free(config->symbol); 340 | 341 | if (config->derivative.derivative_datasource != NULL){ 342 | free(config->derivative.derivative_datasource); 343 | } 344 | 345 | 346 | if (config->n_callbacks != 0) { 347 | for (size_t i = 0; i < config->n_callbacks; i++) 348 | { 349 | free(config->callbacks[i]); 350 | } 351 | } 352 | 353 | 354 | } 355 | 356 | void free_algoticks_settings(algoticks_settings *settings){ 357 | free(settings->config_f); 358 | free(settings->benchmark_f); 359 | free(settings->socket_port); 360 | } 361 | 362 | void free_algoticks_benchmark(algoticks_benchmarkconfig *benchmark){ 363 | 364 | free(benchmark->symbol); 365 | 366 | for (size_t i = 0; i < benchmark->n_algo; i++) 367 | { 368 | free(benchmark->algo_arr[i]); 369 | } 370 | free(benchmark->algo_arr); 371 | 372 | 373 | for (size_t i = 0; i < benchmark->n_datasource; i++) 374 | { 375 | free(benchmark->datasource_arr[i]); 376 | } 377 | free(benchmark->datasource_arr); 378 | 379 | if (benchmark->derivative.derivative_datasource != NULL){ 380 | free(benchmark->derivative.derivative_datasource); 381 | } 382 | 383 | free(benchmark->candles_arr); 384 | free(benchmark->interval_arr); 385 | free(benchmark->target_arr); 386 | free(benchmark->stoploss_arr); 387 | free(benchmark->is_trailing_sl); 388 | free(benchmark->trailing_sl_val_arr); 389 | free(benchmark->quantity_arr); 390 | free(benchmark->sliding); 391 | free(benchmark->intraday); 392 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Jagadeesh Kotra 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/sim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../include/dtypes.h" 8 | #include "../include/csvutils.h" 9 | #include "../include/dashboard.h" 10 | #include "../include/timeutils.h" 11 | #include "../include/sim.h" 12 | #include "../include/misc.h" 13 | #include "../include/debug.h" 14 | #include "../include/callbacks.h" 15 | 16 | 17 | algoticks_simresult run_sim(algoticks_settings *settings, algoticks_config *config){ 18 | 19 | // open and read CSV file. 20 | FILE *fp; 21 | assert(config->datasource != NULL); 22 | fp = fopen(config->datasource, "rb"); 23 | int curr = 0; 24 | 25 | // exit if file cannot be opened. 26 | if (fp == NULL) 27 | { 28 | printf("cannot Read datasource: %s \n", config->datasource); 29 | exit(1); 30 | } 31 | 32 | struct Row storage; 33 | 34 | struct SimResult simresult = {0}; 35 | struct PositionResult positionresult = {0}; 36 | 37 | //add config to simresult. required for writing result to csv. 38 | simresult.config = *config; 39 | 40 | algo_func analyze = load_algo_func(config->algo); 41 | load_callbacks(config); 42 | 43 | //initialize and malloc for series 44 | struct Row* series; 45 | series = (algoticks_row*) malloc((config->candles) * sizeof(algoticks_row)); 46 | 47 | 48 | while (curr != EOF) 49 | { 50 | 51 | for (int i = 0; i < config->candles && curr != -1; i++) 52 | { 53 | curr = read_csv(settings, config, fp, config->datasource, &series[i], curr); 54 | debug_msg(settings->debug, settings->debug_level, 3, __FILE__, __FUNCTION__, __LINE__, series[i].date); 55 | } 56 | 57 | if (curr == -1){ 58 | break; 59 | } 60 | 61 | curr = read_csv(settings, config, fp, config->datasource, &storage, curr); 62 | 63 | struct Signal signal; 64 | signal = analyze(series, config->candles); 65 | 66 | if (signal.buy == true) 67 | { 68 | simresult.buy_signals += 1; 69 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Buy"); 70 | } 71 | else if (signal.sell == true) 72 | { 73 | simresult.sell_signals += 1; 74 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Sell"); 75 | } 76 | else if (signal.neutral == true) 77 | { 78 | simresult.neutral_signals += 1; 79 | debug_msg(settings->debug, settings->debug_level, 2, __FILE__, __FUNCTION__, __LINE__, "Neutral"); 80 | } 81 | else 82 | { 83 | printf("Unknown Signal received!\n"); 84 | exit(1); 85 | } 86 | 87 | if (signal.neutral != true) 88 | { 89 | { 90 | algoticks_event ev = make_event_from_signal(signal); 91 | send_callbacks(ev); 92 | } 93 | 94 | if (config->intraday == true){ 95 | if (is_date_over_or_eq_intraday(storage.date, settings->intraday_hour, settings->intraday_min) == true){ 96 | continue; 97 | } 98 | } 99 | 100 | positionresult = take_position(signal, fp, curr, settings, config, storage); 101 | curr = positionresult.curr; 102 | 103 | simresult.pnl += positionresult.pnl; 104 | 105 | //update peak and bottom; 106 | if (simresult.pnl > simresult.peak) 107 | { 108 | simresult.peak = simresult.pnl; 109 | } 110 | if (simresult.pnl < simresult.bottom) 111 | { 112 | simresult.bottom = simresult.pnl; 113 | } 114 | 115 | if (strcmp(positionresult.hit_type, "T") == 0) 116 | { 117 | simresult.trgt_hits += 1; 118 | if (signal.buy == true){ simresult.b_trgt_hits += 1; } 119 | else if (signal.sell == true){ simresult.s_trgt_hits += 1; } 120 | } 121 | else if (strcmp(positionresult.hit_type, "SL") == 0) 122 | { 123 | simresult.sl_hits += 1; 124 | if (signal.buy == true){ simresult.b_sl_hits += 1; } 125 | else if (signal.sell == true){ simresult.s_sl_hits += 1; } 126 | } 127 | else 128 | { 129 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Position did not hit any boundary"); 130 | } 131 | 132 | //send callback 133 | algoticks_event ev = make_event_from_positionresult(positionresult); 134 | send_callbacks(ev); 135 | 136 | if (positionresult.eof == true) 137 | { 138 | break; 139 | } 140 | else 141 | { 142 | continue; 143 | } 144 | 145 | continue; 146 | } 147 | 148 | // -1 from back +1 from front. easy way to do it it set curr to 1st index of series. 149 | if (config->sliding == true && (config->candles > 2) == true){ 150 | if (curr != -1){ 151 | curr = series[0].curr; 152 | } 153 | } 154 | 155 | //zero out storage 156 | memset(&storage, 0, sizeof(storage)); 157 | 158 | //zero out series 159 | for (int i = 0; i < config->candles; i++) 160 | { 161 | memset(&series[i], 0, sizeof(series[i])); 162 | } 163 | 164 | //print simresult.pnl 165 | char pnl[32]; 166 | sprintf(pnl, "%f", simresult.pnl); 167 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, pnl); 168 | } 169 | 170 | //close datasource file. 171 | fclose(fp); 172 | 173 | //close algo 174 | close_algo_func(); 175 | close_callbacks(); 176 | 177 | //free series mem. 178 | free(series); 179 | 180 | write_simresult_to_csv(&simresult); 181 | 182 | return simresult; 183 | } 184 | 185 | algoticks_positionresult take_position(algoticks_signal signal, FILE *fp, int curr, algoticks_settings *settings, algoticks_config *config, algoticks_row lastrow){ 186 | 187 | //declare buffer for debug messages 188 | char *debug_msg_buffer; 189 | debug_msg_buffer = (char*)malloc(512 * sizeof(char)); 190 | 191 | //initialize pos res. 192 | struct PositionResult positionresult = {0}; 193 | 194 | struct Dashboard dashboard; 195 | 196 | //set dashboard values 197 | dashboard.a = lastrow.close; 198 | dashboard.q = config->quantity; 199 | 200 | if (signal.buy == true) 201 | { 202 | dashboard.is_short = 0; 203 | } 204 | else if (signal.sell == true) 205 | { 206 | dashboard.is_short = 1; 207 | } 208 | else 209 | { 210 | printf("Invalid signal!\n"); 211 | exit(1); 212 | } 213 | 214 | //filter targets 215 | filter_boundaries(config, dashboard.is_short); 216 | 217 | struct Row pos_storage; 218 | 219 | // infinite loop 220 | while (true) 221 | { 222 | positionresult.n_steps++; 223 | 224 | if (curr == EOF || curr == -1) 225 | { 226 | if (settings->debug) 227 | { 228 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, config->datasource); 229 | sprintf(debug_msg_buffer, "%d", curr); 230 | debug_msg(settings->debug, settings->debug_level, 2, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 231 | } 232 | positionresult.pnl = getPnL(dashboard); 233 | 234 | if (positionresult.pnl > 0) 235 | { 236 | strncpy(positionresult.hit_type, "T", 4); 237 | } 238 | else{ 239 | strncpy(positionresult.hit_type, "SL", 4); 240 | } 241 | 242 | 243 | positionresult.eof = true; 244 | break; 245 | } 246 | 247 | curr = read_csv(settings,config, fp, config->datasource, &pos_storage, curr); 248 | 249 | if ((pos_storage.date == NULL) || (pos_storage.close == 0)) 250 | { 251 | continue; 252 | } 253 | 254 | dashboard.b = pos_storage.close; 255 | strncpy(dashboard.date, pos_storage.date, 32); 256 | positionresult.curr = curr; 257 | 258 | if (settings->print == true) 259 | { 260 | print_dashboard(settings, config, dashboard); 261 | } 262 | 263 | //intraday check condition. 264 | if (config->intraday) 265 | { 266 | 267 | //check if over intraday squareoff time! 268 | int intraday_check = is_date_over_or_eq_intraday(pos_storage.date, settings->intraday_hour, settings->intraday_min); 269 | 270 | if (intraday_check == true) 271 | { 272 | sprintf(debug_msg_buffer, "H: %d S: %d Date: %s", settings->intraday_hour, settings->intraday_min, pos_storage.date); 273 | debug_msg(settings->debug, settings->debug_level, 2, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 274 | 275 | positionresult.pnl = getPnL(dashboard); 276 | 277 | if (positionresult.pnl > 0) 278 | { 279 | strncpy(positionresult.hit_type, "T", 4); 280 | } 281 | else 282 | { 283 | strncpy(positionresult.hit_type, "SL", 4); 284 | } 285 | 286 | break; 287 | } 288 | } 289 | 290 | if (is_target_hit(dashboard, config->target) == true) 291 | { 292 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Target Hit!"); 293 | 294 | strncpy(positionresult.hit_type, "T", 4); 295 | positionresult.pnl = getPnL(dashboard); 296 | 297 | if (config->is_trailing_sl) 298 | { 299 | config->target = (dashboard.b - dashboard.a) + config->trailing_sl_val; 300 | 301 | if (dashboard.is_short) 302 | { 303 | config->stoploss += config->trailing_sl_val; 304 | } 305 | else 306 | { 307 | config->stoploss -= -config->trailing_sl_val; 308 | } 309 | 310 | sprintf(debug_msg_buffer, "T:%f SL:%f", config->target, config->stoploss); 311 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 312 | 313 | continue; 314 | } 315 | 316 | break; 317 | } 318 | 319 | if (is_stoploss_hit(dashboard, config->stoploss) == true) 320 | { 321 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "SL Hit!"); 322 | 323 | positionresult.pnl = getPnL(dashboard); 324 | 325 | if (positionresult.pnl > 0) 326 | { 327 | strncpy(positionresult.hit_type, "T", 4); 328 | } 329 | else 330 | { 331 | strncpy(positionresult.hit_type, "SL", 4); 332 | } 333 | 334 | break; 335 | } 336 | 337 | 338 | //send callback from pos 339 | algoticks_event ev = make_event_from_position(pos_storage, dashboard); 340 | send_callbacks(ev); 341 | 342 | 343 | //zero out pos_stotage 344 | memset(&pos_storage, 0, sizeof(pos_storage)); 345 | } 346 | 347 | if (positionresult.n_steps > 0){ 348 | sprintf(debug_msg_buffer, "%f", positionresult.pnl); 349 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 350 | } 351 | 352 | free(debug_msg_buffer); 353 | positionresult.lastrow = pos_storage; 354 | return positionresult; 355 | } -------------------------------------------------------------------------------- /src/csvutils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../include/dtypes.h" 7 | #include "../include/csvutils.h" 8 | #include "../include/debug.h" 9 | #include "../include/misc.h" 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | 19 | int is_header_skipped = false; 20 | 21 | void reset_header_skip(){ 22 | is_header_skipped = false; 23 | } 24 | 25 | 26 | int is_socket_init = false; 27 | void* client_d = 0; 28 | 29 | void* socket_init(char *port){ 30 | 31 | void *context = zmq_ctx_new (); 32 | void *responder = zmq_socket (context, ZMQ_STREAM); 33 | char addr[18] = "tcp://127.0.0.1:"; 34 | strcat(addr, port); //addr+port 35 | 36 | int rc = zmq_bind (responder, addr); 37 | assert (rc == 0); 38 | printf("* TCP socket listening at %s\n", addr); 39 | 40 | return responder; 41 | } 42 | 43 | int check_row_integrity(algoticks_row *row){ 44 | //return true if all ok else false if close is 0 or date is NULL 45 | if (row->close == 0 || row->date == NULL){ 46 | return false; 47 | } 48 | else{ 49 | return true; 50 | } 51 | } 52 | 53 | /* 54 | Example Header: 55 | date,open,close,volume,high,low 56 | 57 | 0->0 -> header_template[0]="date" 58 | 1->1 -> header_template[1]="open" 59 | 2->4 -> header_template[2]="high" 60 | 3->5 -> header_template[3]="low" 61 | 4->2 -> header_template[4]="close" 62 | 5->3 -> header_template[5]="volume" 63 | 64 | header_map = {0,1,4,5,2,3,(Optional: technical_indicator n)} 65 | 66 | technical_indicators are optional! it is by default set to NULL. 67 | */ 68 | 69 | char header_template[MAXCSVHEAD][10] = {"date", "open", "high", "low", "close", "volume", "ti1", "ti2", "ti3", "ti_others"}; 70 | int header_map[MAXCSVHEAD] = {-1}; 71 | 72 | char *get_relative_header_mapping(int index) 73 | { 74 | if ( (index < MAXCSVHEAD) && (index != -1) ) 75 | { 76 | return header_template[header_map[index]]; 77 | } 78 | 79 | return NULL; 80 | } 81 | 82 | char *get_header_mapping(int index) 83 | { 84 | if ( (index < MAXCSVHEAD) && (index != -1) ) 85 | { 86 | return header_template[index]; 87 | } 88 | 89 | return NULL; 90 | } 91 | 92 | bool is_mapped(int key, char *header_name) 93 | { 94 | 95 | char* header = get_relative_header_mapping(key); 96 | if (!header){ 97 | return NULL; 98 | } 99 | return strcmp(header, header_name) == 0; 100 | 101 | } 102 | 103 | 104 | struct stat stat_info; 105 | unsigned int datasource_lastmodified = -1; 106 | 107 | int change_in_modified_date(char* filename){ 108 | 109 | 110 | if (stat(filename, &stat_info) != 1){ 111 | 112 | //if datasource_lastmodified is -1 then it's initial check, update last it to last modfied 113 | if (datasource_lastmodified == -1) { 114 | datasource_lastmodified = stat_info.st_mtime; 115 | return false; 116 | } 117 | 118 | // if file modified 119 | if (datasource_lastmodified != stat_info.st_mtime){ 120 | 121 | //update var. 122 | datasource_lastmodified = stat_info.st_mtime; 123 | return true; 124 | } 125 | 126 | } 127 | 128 | return false; 129 | } 130 | 131 | int reopen_datasource(char* filename, FILE** fp, char* mode){ 132 | 133 | if (freopen(filename, mode, *fp) != NULL){ 134 | return true; 135 | } 136 | else{ 137 | return false; 138 | } 139 | 140 | } 141 | 142 | // this checks if the first char of given string starts with quote. 143 | int is_quoted(char *str){ 144 | if ((str[0] == '"') || (str[0] == '\'')){ 145 | return true; 146 | }else{ 147 | return false; 148 | } 149 | } 150 | 151 | algoticks_row tokenize_row(char *row){ 152 | 153 | char *token; 154 | struct Row data; 155 | memset(&data, 0, sizeof(data)); 156 | 157 | int row_pos = 0; 158 | int header_i = 0; 159 | 160 | token = strtok(row, ","); 161 | 162 | 163 | while (token != NULL && header_i < MAXCSVHEAD) 164 | { 165 | 166 | 167 | if ( get_relative_header_mapping(header_i) == NULL ){ 168 | header_i++; 169 | continue; 170 | } 171 | 172 | #ifdef QUOTED_CHECK 173 | if (is_quoted(token) == true) { remove_quotes(token); } 174 | #endif 175 | 176 | if ( is_mapped(header_i, header_template[0]) ) 177 | { 178 | strncpy(data.date, token, 32); 179 | } 180 | else if ( is_mapped(header_i, header_template[1]) ) 181 | { 182 | data.open = atof(token); 183 | } 184 | else if ( is_mapped(header_i, header_template[2]) ) 185 | { 186 | data.high = atof(token); 187 | } 188 | else if ( is_mapped(header_i, header_template[3]) ) 189 | { 190 | data.low = atof(token); 191 | } 192 | else if ( is_mapped(header_i, header_template[4]) ) 193 | { 194 | data.close = atof(token); 195 | } 196 | else if ( is_mapped(header_i, header_template[5]) ) 197 | { 198 | data.volume = atoi(token); 199 | } 200 | 201 | // technical indicators 202 | else if ( is_mapped(header_i, header_template[6]) ) 203 | { 204 | data.technical_indicators.is_ti1_p = true; 205 | data.technical_indicators.ti1 = atof(token); 206 | } 207 | else if ( is_mapped(header_i, header_template[7]) ) 208 | { 209 | data.technical_indicators.is_ti2_p = true; 210 | data.technical_indicators.ti2 = atof(token); 211 | } 212 | else if ( is_mapped(header_i, header_template[8]) ) 213 | { 214 | data.technical_indicators.is_ti3_p = true; 215 | data.technical_indicators.ti3 = atof(token); 216 | } 217 | else if ( is_mapped(header_i, header_template[9]) ) 218 | { 219 | 220 | data.technical_indicators.is_ti_others_p = true; 221 | data.technical_indicators.ti_others = (char *)malloc((strlen(token) + 1) * sizeof(char)); 222 | strcpy(data.technical_indicators.ti_others, token); 223 | } 224 | else 225 | { 226 | // unknown row position. 227 | break; 228 | } 229 | 230 | row_pos++; 231 | header_i++; 232 | token = strtok(NULL, ","); 233 | } 234 | 235 | return data; 236 | 237 | } 238 | 239 | int process_csv_header(algoticks_settings *settings, char *row){ 240 | char *token; 241 | token = strtok(row, ","); 242 | int header_i = 0; 243 | 244 | while (token != NULL && header_i < MAXCSVHEAD) 245 | { 246 | #ifdef CHOMP 247 | chomp(token); 248 | #endif 249 | 250 | convert_to_lowercase(token); 251 | 252 | debug_msg(settings->debug, settings->debug_level, 4, __FILE__, __FUNCTION__, __LINE__, token); 253 | 254 | 255 | if( strcmp(token, header_template[0]) == 0 ){ 256 | header_map[header_i] = 0; } 257 | 258 | else if( strcmp(token, header_template[1]) == 0 ){ 259 | header_map[header_i] = 1; } 260 | 261 | else if( strcmp(token, header_template[2]) == 0 ){ 262 | header_map[header_i] = 2; } 263 | 264 | else if( strcmp(token, header_template[3]) == 0 ){ 265 | 266 | header_map[header_i] = 3; } 267 | 268 | else if( strcmp(token, header_template[4]) == 0 ){ 269 | 270 | header_map[header_i] = 4; } 271 | 272 | else if( strcmp(token, header_template[5]) == 0 ){ 273 | header_map[header_i] = 5; } 274 | 275 | else if( strcmp(token, header_template[6]) == 0 ){ 276 | header_map[header_i] = 6; } 277 | 278 | else if( strcmp(token, header_template[7]) == 0 ){ 279 | header_map[header_i] = 7; } 280 | 281 | else if( strcmp(token, header_template[8]) == 0 ){ 282 | header_map[header_i] = 8; } 283 | 284 | else if( strcmp(token, header_template[9]) == 0 ){ 285 | header_map[header_i] = 9; } 286 | 287 | else { 288 | header_map[header_i] = -1; 289 | } 290 | 291 | header_i++; 292 | token = strtok(NULL, ","); 293 | } 294 | 295 | //skip the 1st row i.e header 296 | is_header_skipped = true; 297 | return true; 298 | } 299 | 300 | void set_ohlcv_as_header() { 301 | 302 | for (int i = 0; i < MAXCSVHEAD; i++) 303 | { 304 | header_map[i] = i; 305 | } 306 | 307 | } 308 | 309 | void changed_cb(uv_handle_t *handle, const char *filename, int events, int status){ 310 | 311 | if (events & UV_CHANGE){ 312 | //printf("changed\n"); 313 | uv_close(handle, NULL); 314 | } 315 | 316 | 317 | } 318 | 319 | int read_csv(algoticks_settings *settings,algoticks_config *config, FILE *fp, char *fname, algoticks_row *storage, int seek_offset){ 320 | while(true) { 321 | if ( feof(fp) ) 322 | { 323 | if (settings->is_live_data == true){ 324 | 325 | if (settings->print != false){ 326 | printf("watching for data in %s\n", config->datasource); 327 | } 328 | 329 | uv_loop_t *loop = uv_default_loop(); 330 | uv_fs_event_t *event = malloc(sizeof(uv_fs_event_t)); 331 | uv_fs_event_init(loop, event); 332 | uv_fs_event_start(event, (uv_fs_event_cb)changed_cb, config->datasource, UV_FS_EVENT); 333 | uv_run(loop, UV_RUN_DEFAULT); 334 | 335 | reopen_datasource(config->datasource, &fp, "rb"); 336 | assert(uv_loop_close(loop) == 0); 337 | 338 | }else if (settings->is_live_data_socket == true){ 339 | 340 | if (!is_socket_init){ 341 | 342 | client_d = socket_init(settings->socket_port); 343 | 344 | if (client_d < 0){ 345 | printf("error creating socket!\n"); 346 | exit(1); 347 | } 348 | 349 | is_socket_init = true; 350 | } 351 | 352 | if (settings->debug && settings->debug_level > 2){ 353 | printf("waiting for new data from 127.0.0.1:%s\n", settings->socket_port); 354 | } 355 | 356 | char buffer[4096]; 357 | 358 | while(true){ 359 | int bytes = zmq_recv (client_d, buffer, 4096, 0); 360 | 361 | if (bytes > 5){ 362 | break; 363 | } 364 | 365 | } 366 | 367 | reopen_datasource(fname, &fp, "a"); 368 | fprintf(fp, "%s", buffer); 369 | reopen_datasource(fname, &fp, "rb"); 370 | 371 | //set seek 372 | fseek(fp, seek_offset, SEEK_SET); 373 | 374 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, config->datasource); 375 | 376 | } 377 | else { 378 | return EOF; 379 | } 380 | } 381 | 382 | if (seek_offset != 0) 383 | { 384 | fseek(fp, seek_offset, SEEK_SET); 385 | } 386 | 387 | 388 | // temp. storage array(s) 389 | char row[MAXCHARPERLINE]; 390 | int curr_sp; 391 | 392 | 393 | if (config->interval > 0 && is_header_skipped == true && settings->is_live_data == false){ 394 | for (int i = 0; i < config->interval; i++) 395 | { 396 | fgets(row, MAXCHARPERLINE, fp); 397 | debug_msg(settings->debug, settings->debug_level, 4, __FILE__, __FUNCTION__, __LINE__, row); 398 | } 399 | 400 | } 401 | 402 | 403 | //read row string from file 404 | fgets(row, MAXCHARPERLINE, fp); 405 | 406 | 407 | //remove white space at end 408 | #ifdef CHOMP 409 | chomp(row); 410 | #endif 411 | 412 | if (!is_header_skipped){ 413 | if (config->skip_header != true){ 414 | process_csv_header(settings, row); 415 | } 416 | else{ 417 | set_ohlcv_as_header(); 418 | } 419 | is_header_skipped = true; 420 | } 421 | 422 | curr_sp = ftell(fp); 423 | 424 | *storage = tokenize_row(row); 425 | 426 | #ifdef CHECK_ROW_INTEGRITY 427 | if (check_row_integrity(storage) == false){ 428 | continue; 429 | } 430 | #endif 431 | 432 | storage->curr = curr_sp; 433 | 434 | return curr_sp; 435 | 436 | } 437 | 438 | } -------------------------------------------------------------------------------- /src/sim_derivative.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../include/dtypes.h" 8 | #include "../include/csvutils.h" 9 | #include "../include/dashboard.h" 10 | #include "../include/timeutils.h" 11 | #include "../include/sim_derivative.h" 12 | #include "../include/misc.h" 13 | #include "../include/debug.h" 14 | #include "../include/callbacks.h" 15 | 16 | int curr_i = 0; 17 | int curr_d = 0; 18 | char *index_datasource_ptr; 19 | char *derivative_datasource_ptr; 20 | 21 | algoticks_simresult run_sim_w_derivative(algoticks_settings *settings, algoticks_config *config) 22 | { 23 | 24 | index_datasource_ptr = config->datasource; 25 | derivative_datasource_ptr = config->derivative.derivative_datasource; 26 | 27 | // open and read CSV file. 28 | FILE *index; 29 | assert(config->datasource != NULL); 30 | index = fopen(config->datasource, "rb"); 31 | 32 | // exit if file cannot be opened. 33 | if (index == NULL) 34 | { 35 | printf("cannot read datasource: %s \n", config->datasource); 36 | exit(1); 37 | } 38 | 39 | // open and read CSV file. 40 | FILE *derivative; 41 | assert(config->derivative.derivative_datasource != NULL); 42 | derivative = fopen(config->derivative.derivative_datasource, "rb"); 43 | 44 | // exit if file cannot be opened. 45 | if (derivative == NULL) 46 | { 47 | printf("cannot read derivative datasource: %s \n", config->derivative.derivative_datasource); 48 | exit(1); 49 | } 50 | 51 | struct Row storage = {0}; 52 | struct SimResult simresult = {0}; 53 | 54 | //add config to simresult. required for writing result to csv. 55 | simresult.config = *config; 56 | 57 | algo_func analyze = load_algo_func(config->algo); 58 | load_callbacks(config); 59 | 60 | //initialize and malloc for series 61 | struct Row *series; 62 | series = (algoticks_row *)malloc((config->candles) * sizeof(algoticks_row)); 63 | 64 | while (curr_i != EOF) 65 | { 66 | 67 | for (int i = 0; i < config->candles && curr_i != -1; i++) 68 | { 69 | curr_i = read_csv(settings, config, index, config->datasource, &series[i], curr_i); 70 | debug_msg(settings->debug, settings->debug_level, 3, __FILE__, __FUNCTION__, __LINE__, series[i].date); 71 | } 72 | 73 | curr_i = read_csv(settings, config, index, config->datasource, &storage, curr_i); 74 | 75 | if (curr_i == -1) 76 | { 77 | break; 78 | } 79 | 80 | struct Signal signal; 81 | signal = analyze(series, config->candles); 82 | 83 | if (signal.buy == true) 84 | { 85 | simresult.buy_signals += 1; 86 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Buy"); 87 | } 88 | else if (signal.sell == true) 89 | { 90 | simresult.sell_signals += 1; 91 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Sell"); 92 | } 93 | else if (signal.neutral == true) 94 | { 95 | simresult.neutral_signals += 1; 96 | debug_msg(settings->debug, settings->debug_level, 2, __FILE__, __FUNCTION__, __LINE__, "Neutral"); 97 | } 98 | else 99 | { 100 | printf("Unknown Signal received!\n"); 101 | exit(1); 102 | } 103 | 104 | if (signal.neutral != true) 105 | { 106 | 107 | { 108 | algoticks_event ev = make_event_from_signal(signal); 109 | send_callbacks(ev); 110 | } 111 | 112 | struct PositionResult positionresult = {0}; 113 | 114 | if (config->intraday == true) 115 | { 116 | if (is_date_over_or_eq_intraday(storage.date, settings->intraday_hour, settings->intraday_min) == true) 117 | { 118 | continue; 119 | } 120 | } 121 | 122 | positionresult = take_position_w_derivative(signal, index, derivative, settings, config, storage); 123 | curr_i = positionresult.curr; 124 | 125 | simresult.pnl += positionresult.pnl; 126 | 127 | //update peak and bottom; 128 | if (simresult.pnl > simresult.peak) 129 | { 130 | simresult.peak = simresult.pnl; 131 | } 132 | if (simresult.pnl < simresult.bottom) 133 | { 134 | simresult.bottom = simresult.pnl; 135 | } 136 | 137 | if (strcmp(positionresult.hit_type, "T") == 0) 138 | { 139 | simresult.trgt_hits += 1; 140 | if (signal.buy == true) 141 | { 142 | simresult.b_trgt_hits += 1; 143 | } 144 | else if (signal.sell == true) 145 | { 146 | simresult.s_trgt_hits += 1; 147 | } 148 | } 149 | else if (strcmp(positionresult.hit_type, "SL") == 0) 150 | { 151 | simresult.sl_hits += 1; 152 | if (signal.buy == true) 153 | { 154 | simresult.b_sl_hits += 1; 155 | } 156 | else if (signal.sell == true) 157 | { 158 | simresult.s_sl_hits += 1; 159 | } 160 | } 161 | else 162 | { 163 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Position did not hit any boundary"); 164 | } 165 | 166 | //send callback 167 | algoticks_event ev = make_event_from_positionresult(positionresult); 168 | send_callbacks(ev); 169 | 170 | if (positionresult.eof == true) 171 | { 172 | break; 173 | } 174 | else 175 | { 176 | continue; 177 | } 178 | 179 | continue; 180 | } 181 | 182 | // -1 from back +1 from front. easy way to do it it set curr to 1st index of series. 183 | if (config->sliding == true && (config->candles > 2) == true) 184 | { 185 | if (curr_i != -1) 186 | { 187 | curr_i = series[0].curr; 188 | } 189 | } 190 | 191 | //zero out storage 192 | memset(&storage, 0, sizeof(storage)); 193 | 194 | //zero out series 195 | for (int i = 0; i < config->candles; i++) 196 | { 197 | memset(&series[i], 0, sizeof(series[i])); 198 | } 199 | 200 | //print simresult.pnl 201 | char pnl[32]; 202 | sprintf(pnl, "%f", simresult.pnl); 203 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, pnl); 204 | } 205 | 206 | //close datasource(s) file. 207 | fclose(index); 208 | fclose(derivative); 209 | 210 | //close algo 211 | close_algo_func(); 212 | close_callbacks(); 213 | 214 | //free series mem. 215 | free(series); 216 | 217 | curr_i = 0; 218 | curr_d = 0; 219 | 220 | write_simresult_to_csv(&simresult); 221 | 222 | return simresult; 223 | } 224 | algoticks_positionresult take_position_w_derivative(algoticks_signal signal, FILE *index_f, FILE *derivative_f, algoticks_settings *settings, algoticks_config *config, algoticks_row lastrow) 225 | { 226 | 227 | //declare buffer for debug messages 228 | char *debug_msg_buffer; 229 | debug_msg_buffer = (char *)malloc(512 * sizeof(char)); 230 | 231 | struct Row pos_storage; 232 | 233 | //initialize pos res. 234 | struct PositionResult positionresult = {0}; 235 | 236 | struct Dashboard dashboard; 237 | 238 | //reset curr that matches derivate 239 | curr_d = sync_curr(settings, config, derivative_f, config->derivative.derivative_datasource, lastrow.date, curr_d, settings->debug); 240 | if (curr_d == -1) 241 | { 242 | printf("Error: %s not in file %s", lastrow.date, config->derivative.derivative_datasource); 243 | positionresult.eof = true; 244 | return positionresult; 245 | } 246 | 247 | curr_d = read_csv(settings, config, derivative_f, config->derivative.derivative_datasource, &pos_storage, curr_d); 248 | 249 | config->datasource = derivative_datasource_ptr; 250 | 251 | //set required details in dashboard 252 | dashboard.a = pos_storage.close; 253 | dashboard.q = config->quantity; 254 | 255 | if (signal.buy == true) 256 | { 257 | dashboard.is_short = 0; 258 | } 259 | else if (signal.sell == true) 260 | { 261 | dashboard.is_short = 1; 262 | } 263 | else 264 | { 265 | printf("Invalid signal!\n"); 266 | exit(1); 267 | } 268 | 269 | //filter targets 270 | filter_boundaries(config, dashboard.is_short); 271 | 272 | // infinite loop 273 | while (true) 274 | { 275 | positionresult.n_steps++; 276 | 277 | if (curr_d == EOF || curr_d == -1) 278 | { 279 | if (settings->debug) 280 | { 281 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, config->datasource); 282 | sprintf(debug_msg_buffer, "%d", curr_d); 283 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 284 | } 285 | positionresult.pnl = getPnL(dashboard); 286 | 287 | if (positionresult.pnl > 0) 288 | { 289 | strncpy(positionresult.hit_type, "T", 4); 290 | } 291 | else 292 | { 293 | strncpy(positionresult.hit_type, "SL", 4); 294 | } 295 | 296 | positionresult.eof = true; 297 | break; 298 | } 299 | 300 | curr_d = read_csv(settings, config, derivative_f, config->derivative.derivative_datasource, &pos_storage, curr_d); 301 | 302 | if ((pos_storage.date == NULL) || (pos_storage.close == 0)) 303 | { 304 | continue; 305 | } 306 | 307 | dashboard.b = pos_storage.close; 308 | strncpy(dashboard.date, pos_storage.date, 32); 309 | positionresult.curr = curr_d; 310 | 311 | if (settings->print == true) 312 | { 313 | print_dashboard(settings, config, dashboard); 314 | } 315 | 316 | //intraday check condition. 317 | if (config->intraday) 318 | { 319 | 320 | //check if over intraday squareoff time! 321 | int intraday_check = is_date_over_or_eq_intraday(pos_storage.date, settings->intraday_hour, settings->intraday_min); 322 | 323 | if (intraday_check == true) 324 | { 325 | sprintf(debug_msg_buffer, "H: %d S: %d Date: %s", settings->intraday_hour, settings->intraday_min, pos_storage.date); 326 | debug_msg(settings->debug, settings->debug_level, 2, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 327 | 328 | positionresult.pnl = getPnL(dashboard); 329 | 330 | if (positionresult.pnl > 0) 331 | { 332 | strncpy(positionresult.hit_type, "T", 4); 333 | } 334 | else 335 | { 336 | strncpy(positionresult.hit_type, "SL", 4); 337 | } 338 | 339 | break; 340 | } 341 | } 342 | 343 | if (is_target_hit(dashboard, config->target) == true) 344 | { 345 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "Target Hit!"); 346 | 347 | strncpy(positionresult.hit_type, "T", 4); 348 | positionresult.pnl = getPnL(dashboard); 349 | 350 | if (config->is_trailing_sl) 351 | { 352 | config->target = (dashboard.b - dashboard.a) + config->trailing_sl_val; 353 | 354 | if (dashboard.is_short) 355 | { 356 | config->stoploss += config->trailing_sl_val; 357 | } 358 | else 359 | { 360 | config->stoploss -= -config->trailing_sl_val; 361 | } 362 | 363 | sprintf(debug_msg_buffer, "T:%f SL:%f", config->target, config->stoploss); 364 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 365 | 366 | continue; 367 | } 368 | 369 | break; 370 | } 371 | 372 | if (is_stoploss_hit(dashboard, config->stoploss) == true) 373 | { 374 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, "SL Hit!"); 375 | 376 | positionresult.pnl = getPnL(dashboard); 377 | 378 | if (positionresult.pnl > 0) 379 | { 380 | strncpy(positionresult.hit_type, "T", 4); 381 | } 382 | else 383 | { 384 | strncpy(positionresult.hit_type, "SL", 4); 385 | } 386 | 387 | break; 388 | } 389 | 390 | { 391 | //send callback from pos 392 | algoticks_event ev = make_event_from_position(pos_storage, dashboard); 393 | send_callbacks(ev); 394 | } 395 | 396 | //zero out pos_stotage 397 | memset(&pos_storage, 0, sizeof(pos_storage)); 398 | } 399 | 400 | if (positionresult.n_steps > 0) 401 | { 402 | sprintf(debug_msg_buffer, "%f", positionresult.pnl); 403 | debug_msg(settings->debug, settings->debug_level, 1, __FILE__, __FUNCTION__, __LINE__, debug_msg_buffer); 404 | } 405 | 406 | //reset curr that matches index 407 | curr_i = sync_curr(settings, config, index_f, index_datasource_ptr, pos_storage.date, curr_i, settings->debug); 408 | if (curr_i == -1) 409 | { 410 | printf("Error: %s not found in %s\n", pos_storage.date, index_datasource_ptr); 411 | positionresult.eof = true; 412 | } 413 | else 414 | { 415 | positionresult.curr = curr_i; 416 | } 417 | 418 | free(debug_msg_buffer); 419 | positionresult.lastrow = pos_storage; 420 | config->datasource = index_datasource_ptr; 421 | return positionresult; 422 | } -------------------------------------------------------------------------------- /src/parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../include/dtypes.h" 8 | #include "../include/debug.h" 9 | 10 | algoticks_settings parse_settings_from_json(char *filename) 11 | { 12 | 13 | 14 | char buffer[4096]; 15 | size_t len; 16 | FILE *fp; 17 | 18 | fp = fopen(filename, "r"); 19 | if (fp == NULL) 20 | { 21 | printf("cannot Read: %s \n", filename); 22 | exit(1); 23 | } 24 | fread(buffer, 1024, 1, fp); 25 | 26 | struct json_object *parsed_json; 27 | struct json_object *print; 28 | struct json_object *colors; 29 | 30 | struct json_object *debug; 31 | struct json_object *debug_level; 32 | 33 | struct json_object *derivative; 34 | struct json_object *benchmark; 35 | 36 | struct json_object *config_f; 37 | struct json_object *benchmark_f; 38 | 39 | struct json_object *is_live_data; 40 | struct json_object *socket; 41 | struct json_object *socket_port; 42 | 43 | struct json_object *intraday_hour; 44 | struct json_object *intraday_min; 45 | 46 | struct Settings settings = {0}; 47 | 48 | parsed_json = json_tokener_parse(buffer); 49 | 50 | if (parsed_json == NULL){ 51 | printf("Invalid JSON!\n"); 52 | exit(1); 53 | } 54 | 55 | json_object_object_get_ex(parsed_json, "print", &print); 56 | json_object_object_get_ex(parsed_json, "colors", &colors); 57 | json_object_object_get_ex(parsed_json, "debug", &debug); 58 | json_object_object_get_ex(parsed_json, "debug_level", &debug_level); 59 | 60 | json_object_object_get_ex(parsed_json, "derivative", &derivative); 61 | json_object_object_get_ex(parsed_json, "benchmark", &benchmark); 62 | 63 | json_object_object_get_ex(parsed_json, "config_f", &config_f); 64 | json_object_object_get_ex(parsed_json, "benchmark_f", &benchmark_f); 65 | 66 | json_object_object_get_ex(parsed_json, "is_live_data", &is_live_data); 67 | json_object_object_get_ex(parsed_json, "socket", &socket); 68 | json_object_object_get_ex(parsed_json, "socket_port", &socket_port); 69 | 70 | json_object_object_get_ex(parsed_json, "intraday_hour", &intraday_hour); 71 | json_object_object_get_ex(parsed_json, "intraday_min", &intraday_min); 72 | 73 | settings.print = json_object_get_boolean(print); 74 | settings.colors = json_object_get_boolean(colors); 75 | settings.debug = json_object_get_boolean(debug); 76 | settings.debug_level = json_object_get_int(debug_level); 77 | 78 | settings.is_derivative = json_object_get_boolean(derivative); 79 | settings.is_benchmark = json_object_get_boolean(benchmark); 80 | 81 | settings.is_live_data = json_object_get_boolean(is_live_data); 82 | 83 | settings.is_live_data_socket = json_object_get_boolean(socket); 84 | 85 | len = json_object_get_string_len(socket_port); 86 | settings.socket_port = (char*) malloc((len+1) * sizeof(char)); 87 | strcpy(settings.socket_port, json_object_get_string(socket_port)); 88 | 89 | len = json_object_get_string_len(config_f); 90 | settings.config_f = (char*) malloc((len+1) * sizeof(char)); 91 | strcpy(settings.config_f, json_object_get_string(config_f)); 92 | 93 | len = json_object_get_string_len(benchmark_f); 94 | settings.benchmark_f = (char*) malloc((len+1) * sizeof(char)); 95 | strcpy(settings.benchmark_f, json_object_get_string(benchmark_f)); 96 | 97 | 98 | settings.intraday_hour = json_object_get_int(intraday_hour); 99 | settings.intraday_min = json_object_get_int(intraday_min); 100 | 101 | //close config file! 102 | fclose(fp); 103 | 104 | //free json obj. 105 | json_object_put(parsed_json); 106 | 107 | return settings; 108 | } 109 | 110 | algoticks_config parse_config_from_json(char *filename) 111 | { 112 | 113 | 114 | char buffer[4096]; 115 | size_t len; 116 | FILE *fp; 117 | 118 | fp = fopen(filename, "r"); 119 | if (fp == NULL) 120 | { 121 | printf("cannot Read: %s \n", filename); 122 | exit(1); 123 | } 124 | fread(buffer, 1024, 1, fp); 125 | 126 | struct json_object *parsed_json; 127 | 128 | struct json_object *algo; 129 | struct json_object *datasource; 130 | struct json_object *derivative; 131 | struct json_object *symbol; 132 | struct json_object *candles; 133 | struct json_object *interval; 134 | struct json_object *callbacks; 135 | 136 | struct json_object *target; 137 | struct json_object *stoploss; 138 | struct json_object *is_training_sl; 139 | struct json_object *trailing_sl_val; 140 | struct json_object *quantity; 141 | 142 | struct json_object *sliding; 143 | struct json_object *intraday; 144 | struct json_object *skip_header; 145 | 146 | struct json_object *tmp; 147 | 148 | struct Config config = {0}; 149 | 150 | parsed_json = json_tokener_parse(buffer); 151 | 152 | if (parsed_json == NULL){ 153 | printf("Invalid JSON!\n"); 154 | exit(1); 155 | } 156 | 157 | json_object_object_get_ex(parsed_json, "algo", &algo); 158 | json_object_object_get_ex(parsed_json, "datasource", &datasource); 159 | json_object_object_get_ex(parsed_json, "symbol", &symbol); 160 | json_object_object_get_ex(parsed_json, "candles", &candles); 161 | json_object_object_get_ex(parsed_json, "interval", &interval); 162 | json_object_object_get_ex(parsed_json, "target", &target); 163 | json_object_object_get_ex(parsed_json, "stoploss", &stoploss); 164 | json_object_object_get_ex(parsed_json, "is_training_sl", &is_training_sl); 165 | json_object_object_get_ex(parsed_json, "trailing_sl_val", &trailing_sl_val); 166 | json_object_object_get_ex(parsed_json, "quantity", &quantity); 167 | json_object_object_get_ex(parsed_json, "sliding", &sliding); 168 | json_object_object_get_ex(parsed_json, "intraday", &intraday); 169 | json_object_object_get_ex(parsed_json, "skip_header", &skip_header); 170 | 171 | 172 | len = json_object_get_string_len(algo); 173 | config.algo = (char*) malloc((len+1) * sizeof(char)); 174 | strcpy(config.algo, json_object_get_string(algo)); 175 | 176 | len = json_object_get_string_len(datasource); 177 | config.datasource = (char*) malloc((len+1) * sizeof(char)); 178 | strcpy(config.datasource, json_object_get_string(datasource)); 179 | 180 | len = json_object_get_string_len(symbol); 181 | config.symbol = (char*) malloc((len+1) * sizeof(char)); 182 | strcpy(config.symbol, json_object_get_string(symbol)); 183 | 184 | 185 | 186 | config.candles = json_object_get_int(candles); 187 | config.interval = json_object_get_int(interval); 188 | config.quantity = json_object_get_int(quantity); 189 | config.target = json_object_get_double(target); 190 | config.stoploss = json_object_get_double(stoploss); 191 | config.is_trailing_sl = json_object_get_boolean(is_training_sl); 192 | config.trailing_sl_val = json_object_get_double(trailing_sl_val); 193 | config.sliding = json_object_get_boolean(sliding); 194 | config.intraday = json_object_get_boolean(intraday); 195 | config.skip_header = json_object_get_boolean(skip_header); 196 | 197 | //parse derivative if exists. 198 | int derivative_exists = json_object_object_get_ex(parsed_json, "derivative", &derivative); 199 | 200 | if (derivative_exists){ 201 | struct json_object *derivative_datasource, *derivative_interval; 202 | 203 | int derivative_datasource_exists = json_object_object_get_ex(derivative, "derivative_datasource", &derivative_datasource); 204 | if (derivative_datasource_exists){ 205 | 206 | len = json_object_get_string_len(derivative_datasource); 207 | config.derivative.derivative_datasource = (char*) malloc((len+1) * sizeof(char)); 208 | strcpy(config.derivative.derivative_datasource, json_object_get_string(derivative_datasource)); 209 | 210 | } 211 | 212 | int derivative_interval_exists = json_object_object_get_ex(derivative, "derivative_interval", &derivative_interval); 213 | 214 | if (derivative_interval_exists) { 215 | config.derivative.derivative_interval = json_object_get_int(derivative_interval); 216 | } 217 | 218 | } 219 | else{ 220 | //set to None 221 | config.derivative.derivative_datasource = NULL; 222 | config.derivative.derivative_interval = 0; 223 | } 224 | 225 | //parse callbacks if exists. 226 | int callbacks_exists = json_object_object_get_ex(parsed_json, "callbacks", &callbacks); 227 | 228 | if (callbacks_exists){ 229 | config.n_callbacks = json_object_array_length(callbacks); 230 | config.callbacks = (char**) malloc(config.n_callbacks * sizeof(char)); 231 | assert (config.n_callbacks <= 6); 232 | 233 | for (int i = 0; i < config.n_callbacks; i++) 234 | { 235 | tmp = json_object_array_get_idx(callbacks, i); 236 | len = json_object_get_string_len(tmp); 237 | config.callbacks[i] = (char*) malloc((len+1) * sizeof(char)); 238 | strcpy(config.callbacks[i], json_object_get_string(tmp)); 239 | printf("%s\n", config.callbacks[i]); 240 | } 241 | } 242 | else{ 243 | config.n_callbacks = 0; 244 | } 245 | 246 | //close config file! 247 | fclose(fp); 248 | 249 | //free json obj. 250 | json_object_put(parsed_json); 251 | 252 | return config; 253 | } 254 | 255 | algoticks_benchmarkconfig parse_benchmark_from_json(char *filename) 256 | { 257 | 258 | 259 | 260 | char buffer[4096]; 261 | size_t len; 262 | FILE *fp; 263 | 264 | fp = fopen(filename, "r"); 265 | if (fp == NULL) 266 | { 267 | printf("cannot Read: %s \n", filename); 268 | exit(1); 269 | } 270 | fread(buffer, 1024, 1, fp); 271 | 272 | struct json_object *parsed_json; 273 | struct json_object *tmp; 274 | 275 | struct json_object *algo; 276 | struct json_object *datasource; 277 | struct json_object *derivative; 278 | struct json_object *symbol; 279 | struct json_object *interval; 280 | 281 | struct json_object *candles; 282 | struct json_object *target; 283 | struct json_object *stoploss; 284 | struct json_object *is_trailing_sl; 285 | struct json_object *trailing_sl_val; 286 | struct json_object *quantity; 287 | 288 | struct json_object *sliding; 289 | struct json_object *intraday; 290 | struct json_object *skip_header; 291 | 292 | struct BenchmarkConfig benchmarkconfig = {0}; 293 | 294 | parsed_json = json_tokener_parse(buffer); 295 | 296 | json_object_object_get_ex(parsed_json, "algo", &algo); 297 | json_object_object_get_ex(parsed_json, "datasource", &datasource); 298 | json_object_object_get_ex(parsed_json, "symbol", &symbol); 299 | json_object_object_get_ex(parsed_json, "candles", &candles); 300 | json_object_object_get_ex(parsed_json, "interval", &interval); 301 | json_object_object_get_ex(parsed_json, "target", &target); 302 | json_object_object_get_ex(parsed_json, "stoploss", &stoploss); 303 | json_object_object_get_ex(parsed_json, "is_trailing_sl", &is_trailing_sl); 304 | json_object_object_get_ex(parsed_json, "trailing_sl_val", &trailing_sl_val); 305 | json_object_object_get_ex(parsed_json, "quantity", &quantity); 306 | json_object_object_get_ex(parsed_json, "sliding", &sliding); 307 | json_object_object_get_ex(parsed_json, "intraday", &intraday); 308 | json_object_object_get_ex(parsed_json, "skip_header", &skip_header); 309 | 310 | len = json_object_get_string_len(symbol); 311 | benchmarkconfig.symbol = (char*) malloc((len+1) * sizeof(char)); 312 | strcpy(benchmarkconfig.symbol, json_object_get_string(symbol)); 313 | 314 | benchmarkconfig.n_algo = json_object_array_length(algo); 315 | benchmarkconfig.algo_arr = (char**) malloc(benchmarkconfig.n_algo * sizeof(char)); 316 | 317 | for (size_t i = 0; i < benchmarkconfig.n_algo; i++) 318 | { 319 | tmp = json_object_array_get_idx(algo, i); 320 | len = json_object_get_string_len(tmp); 321 | benchmarkconfig.algo_arr[i] = (char*) malloc((len+1) * sizeof(char)); 322 | strcpy(benchmarkconfig.algo_arr[i], json_object_get_string(tmp)); 323 | } 324 | 325 | 326 | benchmarkconfig.n_datasource = json_object_array_length(datasource); 327 | benchmarkconfig.datasource_arr = (char**) malloc(benchmarkconfig.n_datasource * sizeof(char)); 328 | 329 | for (size_t i = 0; i < benchmarkconfig.n_datasource; i++) 330 | { 331 | tmp = json_object_array_get_idx(datasource, i); 332 | len = json_object_get_string_len(tmp); 333 | benchmarkconfig.datasource_arr[i] = (char*) malloc((len+1) * sizeof(char)); 334 | strcpy(benchmarkconfig.datasource_arr[i], json_object_get_string(tmp)); 335 | } 336 | 337 | /* DERIVATIVE */ 338 | int derivative_exists = json_object_object_get_ex(parsed_json, "derivative", &derivative); 339 | 340 | if (derivative_exists){ 341 | struct json_object *derivative_datasource, *derivative_interval; 342 | 343 | int derivative_datasource_exists = json_object_object_get_ex(derivative, "derivative_datasource", &derivative_datasource); 344 | if (derivative_datasource_exists){ 345 | 346 | len = json_object_get_string_len(derivative_datasource); 347 | benchmarkconfig.derivative.derivative_datasource = (char*) malloc((len+1) * sizeof(char)); 348 | strcpy(benchmarkconfig.derivative.derivative_datasource, json_object_get_string(derivative_datasource)); 349 | 350 | } 351 | 352 | int derivative_interval_exists = json_object_object_get_ex(derivative, "derivative_interval", &derivative_interval); 353 | 354 | if (derivative_interval_exists) { 355 | benchmarkconfig.derivative.derivative_interval = json_object_get_int(derivative_interval); 356 | } 357 | 358 | } 359 | else{ 360 | //set to None 361 | benchmarkconfig.derivative.derivative_datasource = NULL; 362 | benchmarkconfig.derivative.derivative_interval = 0; 363 | } 364 | 365 | /* CANDLES */ 366 | benchmarkconfig.n_candles = json_object_array_length(candles); 367 | benchmarkconfig.candles_arr = (int*) malloc(benchmarkconfig.n_candles * sizeof(int)); 368 | 369 | for (size_t i = 0; i < benchmarkconfig.n_candles; i++) 370 | { 371 | tmp = json_object_array_get_idx(candles, i); 372 | len = json_object_get_string_len(tmp); 373 | benchmarkconfig.candles_arr[i] = json_object_get_int(tmp); 374 | } 375 | 376 | /* INTERVAL */ 377 | benchmarkconfig.n_interval = json_object_array_length(interval); 378 | benchmarkconfig.interval_arr = (int*) malloc(benchmarkconfig.n_interval * sizeof(int)); 379 | 380 | for (size_t i = 0; i < benchmarkconfig.n_interval; i++) 381 | { 382 | tmp = json_object_array_get_idx(interval, i); 383 | benchmarkconfig.interval_arr[i] = json_object_get_int(tmp); 384 | } 385 | 386 | 387 | /* TARGET */ 388 | 389 | benchmarkconfig.n_target = json_object_array_length(target); 390 | benchmarkconfig.target_arr = (double*) malloc(benchmarkconfig.n_target * sizeof(double)); 391 | 392 | for (size_t i = 0; i < benchmarkconfig.n_target; i++) 393 | { 394 | tmp = json_object_array_get_idx(target, i); 395 | benchmarkconfig.target_arr[i] = json_object_get_double(tmp); 396 | } 397 | 398 | 399 | /* STOPLOSS */ 400 | 401 | benchmarkconfig.n_stoploss = json_object_array_length(stoploss); 402 | benchmarkconfig.stoploss_arr = (double*) malloc(benchmarkconfig.n_stoploss * sizeof(double)); 403 | 404 | for (size_t i = 0; i < benchmarkconfig.n_stoploss; i++) 405 | { 406 | tmp = json_object_array_get_idx(stoploss, i); 407 | benchmarkconfig.stoploss_arr[i] = json_object_get_double(tmp); 408 | } 409 | 410 | /* TRAILING SL */ 411 | 412 | len = json_object_array_length(is_trailing_sl); 413 | benchmarkconfig.n_is_trailing_sl = len; 414 | bool trailing_sl_true; 415 | benchmarkconfig.is_trailing_sl = (int*) malloc(len * sizeof(int)); 416 | for (size_t i = 0; i < len; i++) 417 | { 418 | tmp = json_object_array_get_idx(is_trailing_sl, i); 419 | benchmarkconfig.is_trailing_sl[i] = json_object_get_boolean(tmp); 420 | 421 | if (benchmarkconfig.is_trailing_sl[i] == true){ 422 | trailing_sl_true = true; 423 | } 424 | } 425 | 426 | 427 | if (trailing_sl_true){ 428 | benchmarkconfig.n_trailing_sl_val = json_object_array_length(trailing_sl_val); 429 | benchmarkconfig.trailing_sl_val_arr = (double*) malloc(benchmarkconfig.n_trailing_sl_val * sizeof(double)); 430 | 431 | for (size_t i = 0; i < benchmarkconfig.n_trailing_sl_val; i++) 432 | { 433 | tmp = json_object_array_get_idx(trailing_sl_val, i); 434 | benchmarkconfig.trailing_sl_val_arr[i] = json_object_get_double(tmp); 435 | } 436 | 437 | } 438 | 439 | /* QUANTITY */ 440 | 441 | benchmarkconfig.n_quantity = json_object_array_length(quantity); 442 | benchmarkconfig.quantity_arr = (int*) malloc(benchmarkconfig.n_quantity* sizeof(int)); 443 | 444 | for (size_t i = 0; i < benchmarkconfig.n_quantity; i++) 445 | { 446 | tmp = json_object_array_get_idx(quantity, i); 447 | benchmarkconfig.quantity_arr[i] = json_object_get_int(tmp); 448 | } 449 | 450 | 451 | /* SLIDING */ 452 | 453 | len = json_object_array_length(sliding); 454 | benchmarkconfig.n_sliding = len; 455 | benchmarkconfig.sliding = (int*) malloc(len * sizeof(int)); 456 | for (size_t i = 0; i < len; i++) 457 | { 458 | tmp = json_object_array_get_idx(sliding, i); 459 | benchmarkconfig.sliding[i] = json_object_get_boolean(tmp); 460 | } 461 | 462 | /* INTRADAY */ 463 | 464 | len = json_object_array_length(intraday); 465 | benchmarkconfig.n_intraday = len; 466 | benchmarkconfig.intraday = (int*) malloc(len * sizeof(int)); 467 | for (size_t i = 0; i < len; i++) 468 | { 469 | tmp = json_object_array_get_idx(intraday, i); 470 | benchmarkconfig.intraday[i] = json_object_get_boolean(tmp); 471 | } 472 | 473 | /* SKIP_HEADER */ 474 | 475 | benchmarkconfig.skip_header = json_object_get_boolean(skip_header); 476 | 477 | //close config file! 478 | fclose(fp); 479 | 480 | //free json obj. 481 | json_object_put(parsed_json); 482 | 483 | return benchmarkconfig; 484 | } --------------------------------------------------------------------------------